Compare commits
176 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e77ea63577 | ||
|
|
097df92c1c | ||
|
|
be669059fb | ||
|
|
dfdd371c52 | ||
|
|
e1e8627168 | ||
|
|
0916c1c3ef | ||
|
|
11656f7bd5 | ||
|
|
483d092af8 | ||
|
|
60aa63b68a | ||
|
|
eb547bd4fd | ||
|
|
0dfde6e284 | ||
|
|
2d7038e03e | ||
|
|
4f0a3a9e4d | ||
|
|
a6897963d5 | ||
|
|
c14803e3fa | ||
|
|
cdba8481c2 | ||
|
|
64ca596a11 | ||
|
|
e1758d107e | ||
|
|
3390182fc9 | ||
|
|
912a43ee3d | ||
|
|
4020304945 | ||
|
|
0a2fc78fea | ||
|
|
b91acfb457 | ||
|
|
b9a0179a03 | ||
|
|
5ed48ccdc8 | ||
|
|
f32d67c8b9 | ||
|
|
0752c7242d | ||
|
|
be855c696b | ||
|
|
da9b5201c4 | ||
|
|
4daa6ef4e4 | ||
|
|
4ad7c55618 | ||
|
|
79e08a2541 | ||
|
|
1c9c80ba93 | ||
|
|
25cb05c876 | ||
|
|
694fbab074 | ||
|
|
2fd28434dd | ||
|
|
d15556b152 | ||
|
|
38d8bab770 | ||
|
|
52f0f8657e | ||
|
|
aedf5c895a | ||
|
|
117f9e4abe | ||
|
|
604fea9fc1 | ||
|
|
994bfd4591 | ||
|
|
02722923b1 | ||
|
|
d63a93429b | ||
|
|
b93e216dc7 | ||
|
|
95a29b6e67 | ||
|
|
272f01a153 | ||
|
|
1a345ae7fc | ||
|
|
c5c138b8eb | ||
|
|
da20e33414 | ||
|
|
7fbe3737bc | ||
|
|
f63cec9c95 | ||
|
|
3063ad83ec | ||
|
|
78680e5bee | ||
|
|
55b9faeb48 | ||
|
|
72645dfeab | ||
|
|
3223de5598 | ||
|
|
1afa4e8e75 | ||
|
|
6fd3736da2 | ||
|
|
7e043e5e6f | ||
|
|
d1585c42b9 | ||
|
|
fc494e3527 | ||
|
|
b344cc9415 | ||
|
|
38b71a9298 | ||
|
|
769ee73240 | ||
|
|
1df2f14c64 | ||
|
|
eab9a0e139 | ||
|
|
b86ac6739a | ||
|
|
9840d9e59d | ||
|
|
0ec52157df | ||
|
|
f1c5330b65 | ||
|
|
306ec8de04 | ||
|
|
6d7c9893d4 | ||
|
|
6264709054 | ||
|
|
76c2077f47 | ||
|
|
a63b1efc29 | ||
|
|
9863c3fca8 | ||
|
|
6fb97f44cf | ||
|
|
f64c448329 | ||
|
|
df6cbc5ec6 | ||
|
|
0f0af751e4 | ||
|
|
6c9dca55bc | ||
|
|
d71e807401 | ||
|
|
7df9ddfe4e | ||
|
|
4170659359 | ||
|
|
2940a7fdfa | ||
|
|
dadd6650ed | ||
|
|
c5a21354af | ||
|
|
8bafe2a482 | ||
|
|
42f1716b48 | ||
|
|
6ab2841dbb | ||
|
|
0f54657377 | ||
|
|
79e5b36551 | ||
|
|
074868d77e | ||
|
|
3dd16a9458 | ||
|
|
62c23ab5fa | ||
|
|
11c05beece | ||
|
|
7b3ef43127 | ||
|
|
e0080e5f75 | ||
|
|
e1ba54bd12 | ||
|
|
7032dfb4f1 | ||
|
|
14e7f7c1f4 | ||
|
|
9ed6f1e419 | ||
|
|
b268c39758 | ||
|
|
4dd386b807 | ||
|
|
b7251f1654 | ||
|
|
780d3e65ad | ||
|
|
cc8cac200f | ||
|
|
e7be5c8ac5 | ||
|
|
8f52864899 | ||
|
|
47a630721a | ||
|
|
10ae6de111 | ||
|
|
2b47f3e56b | ||
|
|
d60dd1b60e | ||
|
|
2822f7ca64 | ||
|
|
ff6afeaf78 | ||
|
|
74852d406c | ||
|
|
921642dc7b | ||
|
|
5c01d44ee9 | ||
|
|
135704dcc8 | ||
|
|
88793bb6c2 | ||
|
|
70a51b3aff | ||
|
|
340a582be7 | ||
|
|
5f66b5466f | ||
|
|
d2169ee567 | ||
|
|
a5c03ba1b7 | ||
|
|
e4ea6426dc | ||
|
|
8bf7cd1dc6 | ||
|
|
92feb3ade7 | ||
|
|
d0e739d8f2 | ||
|
|
4efa6bd75e | ||
|
|
600f15faa0 | ||
|
|
250fa519f9 | ||
|
|
3c6dafcc8e | ||
|
|
8447000eee | ||
|
|
fe453f80ed | ||
|
|
3ff504f03f | ||
|
|
eea9ab6106 | ||
|
|
e9e07c41bb | ||
|
|
17a5e18f46 | ||
|
|
9148bd8b6f | ||
|
|
39766d0f96 | ||
|
|
2d9bca56b2 | ||
|
|
f158d95ce9 | ||
|
|
7a4164ef60 | ||
|
|
f3730a79af | ||
|
|
42eff23a79 | ||
|
|
25bc33350d | ||
|
|
b84d082005 | ||
|
|
1f01ce9615 | ||
|
|
352c5f5ecc | ||
|
|
e5594e860f | ||
|
|
50926faead | ||
|
|
a303afc0e5 | ||
|
|
12607e85e3 | ||
|
|
38fd363e89 | ||
|
|
7f62cfd231 | ||
|
|
c5168bd036 | ||
|
|
be472c5215 | ||
|
|
adac38ecea | ||
|
|
c8b634226e | ||
|
|
ca4cf7cc70 | ||
|
|
b87072bc12 | ||
|
|
04e2bfafbc | ||
|
|
181a32236a | ||
|
|
1f54a8e0a1 | ||
|
|
d63475bb7d | ||
|
|
5a3c5f1523 | ||
|
|
12bc9285f7 | ||
|
|
31df2ea940 | ||
|
|
50b90dd6a4 | ||
|
|
7dd881334d | ||
|
|
530fc8ff3f | ||
|
|
ef460b4d23 | ||
|
|
b591de2ace |
5
.flake8
Normal file
@@ -0,0 +1,5 @@
|
||||
[flake8]
|
||||
max-line-length = 88
|
||||
select = C,E,F,W,B,B9
|
||||
ignore = E203, E501, W503
|
||||
exclude = __init__.py
|
||||
7
.github/actions/get-artifact/Dockerfile
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM python:3.7
|
||||
|
||||
RUN pip install httpx "pydantic==1.5.1"
|
||||
|
||||
COPY ./app /app
|
||||
|
||||
CMD ["python", "/app/main.py"]
|
||||
16
.github/actions/get-artifact/action.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: "Get Artifact"
|
||||
description: "Get artifact, possibly uploaded by a PR, useful to deploy docs previews"
|
||||
author: "Sebastián Ramírez <tiangolo@gmail.com>"
|
||||
inputs:
|
||||
token:
|
||||
description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
|
||||
required: true
|
||||
name:
|
||||
description: 'Artifact name'
|
||||
required: true
|
||||
path:
|
||||
description: 'Where to store the artifact'
|
||||
required: true
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
63
.github/actions/get-artifact/app/main.py
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
import httpx
|
||||
from pydantic import BaseModel, BaseSettings, SecretStr
|
||||
|
||||
github_api = "https://api.github.com"
|
||||
netlify_api = "https://api.netlify.com"
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
input_name: str
|
||||
input_token: SecretStr
|
||||
input_path: str
|
||||
github_repository: str
|
||||
github_event_path: Path
|
||||
github_event_name: Optional[str] = None
|
||||
|
||||
|
||||
class Artifact(BaseModel):
|
||||
id: int
|
||||
node_id: str
|
||||
name: str
|
||||
size_in_bytes: int
|
||||
url: str
|
||||
archive_download_url: str
|
||||
expired: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class ArtifactResponse(BaseModel):
|
||||
total_count: int
|
||||
artifacts: List[Artifact]
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
settings = Settings()
|
||||
logging.info(f"Using config: {settings.json()}")
|
||||
github_headers = {
|
||||
"Authorization": f"token {settings.input_token.get_secret_value()}"
|
||||
}
|
||||
response = httpx.get(
|
||||
f"{github_api}/repos/{settings.github_repository}/actions/artifacts",
|
||||
headers=github_headers,
|
||||
)
|
||||
data = response.json()
|
||||
artifacts_response = ArtifactResponse.parse_obj(data)
|
||||
use_artifact: Optional[Artifact] = None
|
||||
for artifact in artifacts_response.artifacts:
|
||||
if artifact.name == settings.input_name:
|
||||
use_artifact = artifact
|
||||
break
|
||||
assert use_artifact
|
||||
file_response = httpx.get(
|
||||
use_artifact.archive_download_url, headers=github_headers, timeout=30
|
||||
)
|
||||
zip_file = Path(settings.input_path)
|
||||
zip_file.write_bytes(file_response.content)
|
||||
logging.info("Finished")
|
||||
7
.github/actions/watch-previews/Dockerfile
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
FROM python:3.7
|
||||
|
||||
RUN pip install httpx PyGithub "pydantic==1.5.1"
|
||||
|
||||
COPY ./app /app
|
||||
|
||||
CMD ["python", "/app/main.py"]
|
||||
10
.github/actions/watch-previews/action.yml
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
name: "Watch docs previews in PRs"
|
||||
description: "Check PRs and trigger new docs deploys"
|
||||
author: "Sebastián Ramírez <tiangolo@gmail.com>"
|
||||
inputs:
|
||||
token:
|
||||
description: 'Token for the repo. Can be passed in using {{ secrets.GITHUB_TOKEN }}'
|
||||
required: true
|
||||
runs:
|
||||
using: 'docker'
|
||||
image: 'Dockerfile'
|
||||
101
.github/actions/watch-previews/app/main.py
vendored
Normal file
@@ -0,0 +1,101 @@
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
|
||||
import httpx
|
||||
from github import Github
|
||||
from github.NamedUser import NamedUser
|
||||
from pydantic import BaseModel, BaseSettings, SecretStr
|
||||
|
||||
github_api = "https://api.github.com"
|
||||
netlify_api = "https://api.netlify.com"
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
input_token: SecretStr
|
||||
github_repository: str
|
||||
github_event_path: Path
|
||||
github_event_name: Optional[str] = None
|
||||
|
||||
|
||||
class Artifact(BaseModel):
|
||||
id: int
|
||||
node_id: str
|
||||
name: str
|
||||
size_in_bytes: int
|
||||
url: str
|
||||
archive_download_url: str
|
||||
expired: bool
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
|
||||
class ArtifactResponse(BaseModel):
|
||||
total_count: int
|
||||
artifacts: List[Artifact]
|
||||
|
||||
|
||||
def get_message(commit: str) -> str:
|
||||
return f"Docs preview for commit {commit} at"
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
settings = Settings()
|
||||
logging.info(f"Using config: {settings.json()}")
|
||||
g = Github(settings.input_token.get_secret_value())
|
||||
repo = g.get_repo(settings.github_repository)
|
||||
owner: NamedUser = repo.owner
|
||||
headers = {"Authorization": f"token {settings.input_token.get_secret_value()}"}
|
||||
prs = list(repo.get_pulls(state="open"))
|
||||
response = httpx.get(
|
||||
f"{github_api}/repos/{settings.github_repository}/actions/artifacts",
|
||||
headers=headers,
|
||||
)
|
||||
data = response.json()
|
||||
artifacts_response = ArtifactResponse.parse_obj(data)
|
||||
for pr in prs:
|
||||
logging.info("-----")
|
||||
logging.info(f"Processing PR #{pr.number}: {pr.title}")
|
||||
pr_comments = list(pr.get_issue_comments())
|
||||
pr_commits = list(pr.get_commits())
|
||||
last_commit = pr_commits[0]
|
||||
for pr_commit in pr_commits:
|
||||
if pr_commit.commit.author.date > last_commit.commit.author.date:
|
||||
last_commit = pr_commit
|
||||
commit = last_commit.commit.sha
|
||||
logging.info(f"Last commit: {commit}")
|
||||
message = get_message(commit)
|
||||
notified = False
|
||||
for pr_comment in pr_comments:
|
||||
if message in pr_comment.body:
|
||||
notified = True
|
||||
logging.info(f"Docs preview was notified: {notified}")
|
||||
if not notified:
|
||||
artifact_name = f"docs-zip-{commit}"
|
||||
use_artifact: Optional[Artifact] = None
|
||||
for artifact in artifacts_response.artifacts:
|
||||
if artifact.name == artifact_name:
|
||||
use_artifact = artifact
|
||||
break
|
||||
if not use_artifact:
|
||||
logging.info("Artifact not available")
|
||||
else:
|
||||
logging.info(f"Existing artifact: {use_artifact.name}")
|
||||
response = httpx.post(
|
||||
"https://api.github.com/repos/tiangolo/fastapi/actions/workflows/preview-docs.yml/dispatches",
|
||||
headers=headers,
|
||||
json={
|
||||
"ref": "master",
|
||||
"inputs": {
|
||||
"pr": f"{pr.number}",
|
||||
"name": artifact_name,
|
||||
"commit": commit,
|
||||
},
|
||||
},
|
||||
)
|
||||
logging.info(
|
||||
f"Trigger sent, response status: {response.status_code} - content: {response.content}"
|
||||
)
|
||||
logging.info("Finished")
|
||||
@@ -1,4 +1,4 @@
|
||||
name: Build and Deploy to Netlify
|
||||
name: Build Docs
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
@@ -7,6 +7,10 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
@@ -18,12 +22,21 @@ jobs:
|
||||
run: python3.7 -m flit install --extras doc
|
||||
- name: Build Docs
|
||||
run: python3.7 ./scripts/docs.py build-all
|
||||
- name: Zip docs
|
||||
if: github.event_name == 'pull_request'
|
||||
run: bash ./scripts/zip-docs.sh
|
||||
- uses: actions/upload-artifact@v2
|
||||
if: github.event_name == 'pull_request'
|
||||
with:
|
||||
name: docs-zip-${{ github.event.pull_request.head.sha }}
|
||||
path: ./docs.zip
|
||||
- name: Deploy to Netlify
|
||||
uses: nwtgck/actions-netlify@v1.0.3
|
||||
uses: nwtgck/actions-netlify@v1.1.5
|
||||
with:
|
||||
publish-dir: './site'
|
||||
production-branch: master
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
enable-commit-comment: false
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
14
.github/workflows/pr-approvals.yml
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
name: Label approved pull requests
|
||||
on: pull_request_review
|
||||
jobs:
|
||||
labelWhenApproved:
|
||||
name: Label when approved
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Label when approved
|
||||
uses: pullreminders/label-when-approved-action@v1.0.7
|
||||
env:
|
||||
APPROVALS: "2"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ADD_LABEL: "approved-2"
|
||||
REMOVE_LABEL: "awaiting%20review"
|
||||
44
.github/workflows/preview-docs.yml
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
name: Preview Docs
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
pr:
|
||||
description: Pull Request number
|
||||
required: true
|
||||
name:
|
||||
description: Artifact name for zip file with docs
|
||||
required: true
|
||||
commit:
|
||||
description: Commit SHA hash
|
||||
required: true
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/get-artifact
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
name: ${{ github.event.inputs.name }}
|
||||
path: ./archive.zip
|
||||
- name: Unzip docs
|
||||
run: bash ./scripts/unzip-docs.sh
|
||||
- name: Deploy to Netlify
|
||||
id: netlify
|
||||
uses: nwtgck/actions-netlify@v1.1.5
|
||||
with:
|
||||
publish-dir: './site'
|
||||
production-deploy: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
enable-commit-comment: false
|
||||
env:
|
||||
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
||||
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
||||
- name: Comment Deploy
|
||||
env:
|
||||
PR: "${{ github.event.inputs.pr }}"
|
||||
DEPLOY_URL: "${{ steps.netlify.outputs.deploy-url }}"
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
COMMIT: "${{ github.event.inputs.commit }}"
|
||||
run: bash ./scripts/docs-comment-deploy.sh
|
||||
39
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
name: Publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types:
|
||||
- created
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: "3.6"
|
||||
- name: Install Flit
|
||||
run: pip install flit
|
||||
- name: Install Dependencies
|
||||
run: flit install --symlink
|
||||
- name: Publish
|
||||
env:
|
||||
FLIT_USERNAME: ${{ secrets.FLIT_USERNAME }}
|
||||
FLIT_PASSWORD: ${{ secrets.FLIT_PASSWORD }}
|
||||
run: bash scripts/publish.sh
|
||||
- name: Dump GitHub context
|
||||
env:
|
||||
GITHUB_CONTEXT: ${{ toJson(github) }}
|
||||
run: echo "$GITHUB_CONTEXT"
|
||||
# - name: Notify
|
||||
# env:
|
||||
# GITTER_TOKEN: ${{ secrets.GITTER_TOKEN }}
|
||||
# GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# TAG: ${{ github.event.release.name }}
|
||||
# run: bash scripts/notify.sh
|
||||
29
.github/workflows/test.yml
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
name: Test
|
||||
|
||||
on:
|
||||
push:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8]
|
||||
fail-fast: false
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install Flit
|
||||
run: pip install flit
|
||||
- name: Install Dependencies
|
||||
run: flit install --symlink
|
||||
- name: Test
|
||||
run: bash scripts/test.sh
|
||||
- name: Upload coverage
|
||||
uses: codecov/codecov-action@v1
|
||||
13
.github/workflows/watch-docs-previews.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Watch Docs Previews
|
||||
on:
|
||||
schedule:
|
||||
- cron: "0 * * * *"
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-18.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./.github/actions/watch-previews
|
||||
with:
|
||||
token: ${{ secrets.ACTIONS_TOKEN }}
|
||||
7
.gitignore
vendored
@@ -16,3 +16,10 @@ Pipfile.lock
|
||||
env3.*
|
||||
env
|
||||
docs_build
|
||||
venv
|
||||
docs.zip
|
||||
archive.zip
|
||||
|
||||
# vim temporary files
|
||||
*~
|
||||
.*.sw?
|
||||
|
||||
32
.travis.yml
@@ -1,32 +0,0 @@
|
||||
dist: xenial
|
||||
|
||||
language: python
|
||||
|
||||
cache: pip
|
||||
|
||||
python:
|
||||
- "3.6"
|
||||
- "3.7"
|
||||
- "3.8"
|
||||
- "nightly"
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- python: "nightly"
|
||||
|
||||
install:
|
||||
- pip install flit
|
||||
- flit install --symlink
|
||||
|
||||
script:
|
||||
- bash scripts/test.sh
|
||||
|
||||
after_script:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
deploy:
|
||||
provider: script
|
||||
script: bash scripts/deploy.sh
|
||||
on:
|
||||
tags: true
|
||||
python: "3.6"
|
||||
26
README.md
@@ -5,14 +5,14 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.com/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.com/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi" alt="Coverage">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://gitter.im/tiangolo/fastapi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge" target="_blank">
|
||||
<img src="https://badges.gitter.im/tiangolo/fastapi.svg" alt="Join the chat at https://gitter.im/tiangolo/fastapi">
|
||||
@@ -131,6 +131,8 @@ $ pip install uvicorn
|
||||
* Create a file `main.py` with:
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -142,7 +144,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -151,7 +153,9 @@ def read_item(item_id: int, q: str = None):
|
||||
|
||||
If your code uses `async` / `await`, use `async def`:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -163,7 +167,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str = None):
|
||||
async def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -241,7 +245,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.
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 23 24 25"
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -251,7 +257,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool = None
|
||||
is_offer: Optional[bool] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -260,7 +266,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
|
||||
230
docs/en/data/external_links.yml
Normal file
@@ -0,0 +1,230 @@
|
||||
articles:
|
||||
english:
|
||||
- link: https://medium.com/@williamhayes/fastapi-starlette-debug-vs-prod-5f7561db3a59
|
||||
title: FastAPI/Starlette debug vs prod
|
||||
author_link: https://medium.com/@williamhayes
|
||||
author: William Hayes
|
||||
- link: https://medium.com/data-rebels/fastapi-google-as-an-external-authentication-provider-3a527672cf33
|
||||
title: FastAPI — Google as an external authentication provider
|
||||
author_link: https://medium.com/@nilsdebruin
|
||||
author: Nils de Bruin
|
||||
- link: https://medium.com/data-rebels/fastapi-how-to-add-basic-and-cookie-authentication-a45c85ef47d3
|
||||
title: FastAPI — How to add basic and cookie authentication
|
||||
author_link: https://medium.com/@nilsdebruin
|
||||
author: Nils de Bruin
|
||||
- link: https://dev.to/errietta/introduction-to-the-fastapi-python-framework-2n10
|
||||
title: Introduction to the fastapi python framework
|
||||
author_link: https://dev.to/errietta
|
||||
author: Errieta Kostala
|
||||
- link: http://nickc1.github.io/api,/scikit-learn/2019/01/10/scikit-fastapi.html
|
||||
title: "FastAPI and Scikit-Learn: Easily Deploy Models"
|
||||
author_link: http://nickc1.github.io/
|
||||
author: Nick Cortale
|
||||
- link: https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680
|
||||
title: "FastAPI authentication revisited: Enabling API key authentication"
|
||||
author_link: https://medium.com/@nilsdebruin
|
||||
author: Nils de Bruin
|
||||
- link: https://medium.com/@nico.axtmann95/deploying-a-scikit-learn-model-with-onnx-und-fastapi-1af398268915
|
||||
title: Deploying a scikit-learn model with ONNX and FastAPI
|
||||
author_link: https://www.linkedin.com/in/nico-axtmann
|
||||
author: Nico Axtmann
|
||||
- link: https://geekflare.com/python-asynchronous-web-frameworks/
|
||||
title: Top 5 Asynchronous Web Frameworks for Python
|
||||
author_link: https://geekflare.com/author/ankush/
|
||||
author: Ankush Thakur
|
||||
- link: https://medium.com/@gntrm/jwt-authentication-with-fastapi-and-aws-cognito-1333f7f2729e
|
||||
title: JWT Authentication with FastAPI and AWS Cognito
|
||||
author_link: https://twitter.com/gntrm
|
||||
author: Johannes Gontrum
|
||||
- link: https://towardsdatascience.com/how-to-deploy-a-machine-learning-model-dc51200fe8cf
|
||||
title: How to Deploy a Machine Learning Model
|
||||
author_link: https://www.linkedin.com/in/mgrootendorst/
|
||||
author: Maarten Grootendorst
|
||||
- link: https://eng.uber.com/ludwig-v0-2/
|
||||
title: "Uber: Ludwig v0.2 Adds New Features and Other Improvements to its Deep Learning Toolbox [including a FastAPI server]"
|
||||
author_link: https://eng.uber.com
|
||||
author: Uber Engineering
|
||||
- link: https://gitlab.com/euri10/fastapi_cheatsheet
|
||||
title: A FastAPI and Swagger UI visual cheatsheet
|
||||
author_link: https://gitlab.com/euri10
|
||||
author: "@euri10"
|
||||
- link: https://medium.com/@mike.p.moritz/using-docker-compose-to-deploy-a-lightweight-python-rest-api-with-a-job-queue-37e6072a209b
|
||||
title: Using Docker Compose to deploy a lightweight Python REST API with a job queue
|
||||
author_link: https://medium.com/@mike.p.moritz
|
||||
author: Mike Moritz
|
||||
- link: https://robwagner.dev/tortoise-fastapi-setup/
|
||||
title: Setting up Tortoise ORM with FastAPI
|
||||
author_link: https://robwagner.dev/
|
||||
author: Rob Wagner
|
||||
- link: https://dev.to/dbanty/why-i-m-leaving-flask-3ki6
|
||||
title: Why I'm Leaving Flask
|
||||
author_link: https://dev.to/dbanty
|
||||
author: Dylan Anthony
|
||||
- link: https://medium.com/python-data/how-to-deploy-tensorflow-2-0-models-as-an-api-service-with-fastapi-docker-128b177e81f3
|
||||
title: How To Deploy Tensorflow 2.0 Models As An API Service With FastAPI & Docker
|
||||
author_link: https://medium.com/@bbrenyah
|
||||
author: Bernard Brenyah
|
||||
- link: https://testdriven.io/blog/fastapi-crud/
|
||||
title: "TestDriven.io: Developing and Testing an Asynchronous API with FastAPI and Pytest"
|
||||
author_link: https://testdriven.io/authors/herman
|
||||
author: Michael Herman
|
||||
- link: https://towardsdatascience.com/deploying-iris-classifications-with-fastapi-and-docker-7c9b83fdec3a
|
||||
title: "Towards Data Science: Deploying Iris Classifications with FastAPI and Docker"
|
||||
author_link: https://towardsdatascience.com/@mandygu
|
||||
author: Mandy Gu
|
||||
- link: https://medium.com/analytics-vidhya/deploy-machine-learning-models-with-keras-fastapi-redis-and-docker-4940df614ece
|
||||
title: Deploy Machine Learning Models with Keras, FastAPI, Redis and Docker
|
||||
author_link: https://medium.com/@shane.soh
|
||||
author: Shane Soh
|
||||
- link: https://medium.com/@arthur393/another-boilerplate-to-fastapi-azure-pipeline-ci-pytest-3c8d9a4be0bb
|
||||
title: "Another Boilerplate to FastAPI: Azure Pipeline CI + Pytest"
|
||||
author_link: https://twitter.com/arthurheinrique
|
||||
author: Arthur Henrique
|
||||
- link: https://iwpnd.pw/articles/2020-01/deploy-fastapi-to-aws-lambda
|
||||
title: How to continuously deploy a FastAPI to AWS Lambda with AWS SAM
|
||||
author_link: https://iwpnd.pw
|
||||
author: Benjamin Ramser
|
||||
- link: https://www.tutlinks.com/create-and-deploy-fastapi-app-to-heroku/
|
||||
title: Create and Deploy FastAPI app to Heroku without using Docker
|
||||
author_link: https://www.linkedin.com/in/navule/
|
||||
author: Navule Pavan Kumar Rao
|
||||
- link: https://iwpnd.pw/articles/2020-03/apache-kafka-fastapi-geostream
|
||||
title: Apache Kafka producer and consumer with FastAPI and aiokafka
|
||||
author_link: https://iwpnd.pw
|
||||
author: Benjamin Ramser
|
||||
- link: https://wuilly.com/2019/10/real-time-notifications-with-python-and-postgres/
|
||||
title: Real-time Notifications with Python and Postgres
|
||||
author_link: https://wuilly.com/
|
||||
author: Guillermo Cruz
|
||||
- link: https://dev.to/paurakhsharma/microservice-in-python-using-fastapi-24cc
|
||||
title: Microservice in Python using FastAPI
|
||||
author_link: https://twitter.com/PaurakhSharma
|
||||
author: Paurakh Sharma Humagain
|
||||
- link: https://dev.to/cuongld2/build-simple-api-service-with-python-fastapi-part-1-581o
|
||||
title: Build simple API service with Python FastAPI — Part 1
|
||||
author_link: https://dev.to/cuongld2
|
||||
author: cuongld2
|
||||
- link: https://paulsec.github.io/posts/fastapi_plus_zeit_serverless_fu/
|
||||
title: FastAPI + Zeit.co = 🚀
|
||||
author_link: https://twitter.com/PaulWebSec
|
||||
author: Paul Sec
|
||||
- link: https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe
|
||||
title: Build a web API from scratch with FastAPI - the workshop
|
||||
author_link: https://twitter.com/tiangolo
|
||||
author: Sebastián Ramírez (tiangolo)
|
||||
- link: https://www.twilio.com/blog/build-secure-twilio-webhook-python-fastapi
|
||||
title: Build a Secure Twilio Webhook with Python and FastAPI
|
||||
author_link: https://www.twilio.com
|
||||
author: Twilio
|
||||
- link: https://www.stavros.io/posts/fastapi-with-django/
|
||||
title: Using FastAPI with Django
|
||||
author_link: https://twitter.com/Stavros
|
||||
author: Stavros Korokithakis
|
||||
- link: https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072
|
||||
title: Introducing Dispatch
|
||||
author_link: https://netflixtechblog.com/
|
||||
author: Netflix
|
||||
- link: https://davidefiocco.github.io/2020/06/27/streamlit-fastapi-ml-serving.html
|
||||
title: Machine learning model serving in Python using FastAPI and streamlit
|
||||
author_link: https://github.com/davidefiocco
|
||||
author: Davide Fiocco
|
||||
- link: https://www.tutlinks.com/deploy-fastapi-on-azure/
|
||||
title: Deploy FastAPI on Azure App Service
|
||||
author_link: https://www.linkedin.com/in/navule/
|
||||
author: Navule Pavan Kumar Rao
|
||||
- link: https://towardsdatascience.com/build-and-host-fast-data-science-applications-using-fastapi-823be8a1d6a0
|
||||
title: Build And Host Fast Data Science Applications Using FastAPI
|
||||
author_link: https://medium.com/@farhadmalik
|
||||
author: Farhad Malik
|
||||
japanese:
|
||||
- link: https://qiita.com/mtitg/items/47770e9a562dd150631d
|
||||
title: FastAPI|DB接続してCRUDするPython製APIサーバーを構築
|
||||
author_link: https://qiita.com/mtitg
|
||||
author: "@mtitg"
|
||||
- link: https://qiita.com/ryoryomaru/items/59958ed385b3571d50de
|
||||
title: python製の最新APIフレームワーク FastAPI を触ってみた
|
||||
author_link: https://qiita.com/ryoryomaru
|
||||
author: "@ryoryomaru"
|
||||
- link: https://qiita.com/angel_katayoku/items/0e1f5dbbe62efc612a78
|
||||
title: FastAPIでCORSを回避
|
||||
author_link: https://qiita.com/angel_katayoku
|
||||
author: "@angel_katayoku"
|
||||
- link: https://qiita.com/angel_katayoku/items/4fbc1a4e2b33fa2237d2
|
||||
title: FastAPIをMySQLと接続してDockerで管理してみる
|
||||
author_link: https://qiita.com/angel_katayoku
|
||||
author: "@angel_katayoku"
|
||||
- link: https://qiita.com/angel_katayoku/items/8a458a8952f50b73f420
|
||||
title: FastAPIでPOSTされたJSONのレスポンスbodyを受け取る
|
||||
author_link: https://qiita.com/angel_katayoku
|
||||
author: "@angel_katayoku"
|
||||
- link: https://qiita.com/hikarut/items/b178af2e2440c67c6ac4
|
||||
title: フロントエンド開発者向けのDockerによるPython開発環境構築
|
||||
author_link: https://qiita.com/hikarut
|
||||
author: Hikaru Takahashi
|
||||
- link: https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-environment
|
||||
title: "【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】"
|
||||
author_link: https://rightcode.co.jp/author/jun
|
||||
author: ライトコードメディア編集部
|
||||
- link: https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-model-building
|
||||
title: "【第2回】FastAPIチュートリアル: ToDoアプリを作ってみよう【モデル構築編】"
|
||||
author_link: https://rightcode.co.jp/author/jun
|
||||
author: ライトコードメディア編集部
|
||||
- link: https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-authentication-user-registration
|
||||
title: "【第3回】FastAPIチュートリアル: toDoアプリを作ってみよう【認証・ユーザ登録編】"
|
||||
author_link: https://rightcode.co.jp/author/jun
|
||||
author: ライトコードメディア編集部
|
||||
- link: https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-admin-page-improvement
|
||||
title: "【第4回】FastAPIチュートリアル: toDoアプリを作ってみよう【管理者ページ改良編】"
|
||||
author_link: https://rightcode.co.jp/author/jun
|
||||
author: ライトコードメディア編集部
|
||||
- link: https://qiita.com/bee2/items/0ad260ab9835a2087dae
|
||||
title: PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)
|
||||
author_link: https://qiita.com/bee2
|
||||
author: "@bee2"
|
||||
- link: https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9
|
||||
title: "[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する"
|
||||
author_link: https://qiita.com/bee2
|
||||
author: "@bee2"
|
||||
vietnamese:
|
||||
- link: https://fullstackstation.com/fastapi-trien-khai-bang-docker/
|
||||
title: "FASTAPI: TRIỂN KHAI BẰNG DOCKER"
|
||||
author_link: https://fullstackstation.com/author/figonking/
|
||||
author: Nguyễn Nhân
|
||||
russian:
|
||||
- link: https://habr.com/ru/post/454440/
|
||||
title: "Мелкая питонячая радость #2: Starlette - Солидная примочка – FastAPI"
|
||||
author_link: https://habr.com/ru/users/57uff3r/
|
||||
author: Andrey Korchak
|
||||
- link: https://habr.com/ru/post/478620/
|
||||
title: Почему Вы должны попробовать FastAPI?
|
||||
author_link: https://github.com/prostomarkeloff
|
||||
author: prostomarkeloff
|
||||
german:
|
||||
- link: https://blog.codecentric.de/2019/08/inbetriebnahme-eines-scikit-learn-modells-mit-onnx-und-fastapi/
|
||||
title: Inbetriebnahme eines scikit-learn-Modells mit ONNX und FastAPI
|
||||
author_link: https://twitter.com/_nicoax
|
||||
author: Nico Axtmann
|
||||
podcasts:
|
||||
english:
|
||||
- link: https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855
|
||||
title: FastAPI on PythonBytes
|
||||
author_link: https://pythonbytes.fm/
|
||||
author: Python Bytes FM
|
||||
- link: https://www.pythonpodcast.com/fastapi-web-application-framework-episode-259/
|
||||
title: "Build The Next Generation Of Python Web Applications With FastAPI - Episode 259 - interview to Sebastían Ramírez (tiangolo)"
|
||||
author_link: https://www.pythonpodcast.com/
|
||||
author: Podcast.`__init__`
|
||||
talks:
|
||||
english:
|
||||
- link: https://www.youtube.com/watch?v=3DLwPcrE5mA
|
||||
title: "PyCon UK 2019: FastAPI from the ground up"
|
||||
author_link: https://twitter.com/chriswithers13
|
||||
author: Chris Withers
|
||||
- link: https://www.youtube.com/watch?v=z9K5pwb0rt8
|
||||
title: "PyConBY 2020: Serve ML models easily with FastAPI"
|
||||
author_link: https://twitter.com/tiangolo
|
||||
author: "Sebastián Ramírez (tiangolo)"
|
||||
- link: https://www.youtube.com/watch?v=PnpTY1f4k2U
|
||||
title: "[VIRTUAL] Py.Amsterdam's flying Software Circus: Intro to FastAPI"
|
||||
author_link: https://twitter.com/tiangolo
|
||||
author: "Sebastián Ramírez (tiangolo)"
|
||||
@@ -23,7 +23,7 @@ Each of those response `dict`s can have a key `model`, containing a Pydantic mod
|
||||
|
||||
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
|
||||
|
||||
```Python hl_lines="18 23"
|
||||
```Python hl_lines="18 23"
|
||||
{!../../../docs_src/additional_responses/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -44,7 +44,7 @@ For example, to declare another response with a status code `404` and a Pydantic
|
||||
|
||||
The generated responses in the OpenAPI for this *path operation* will be:
|
||||
|
||||
```JSON hl_lines="3 4 5 6 7 8 9 10 11 12"
|
||||
```JSON hl_lines="3-12"
|
||||
{
|
||||
"responses": {
|
||||
"404": {
|
||||
@@ -83,7 +83,7 @@ The generated responses in the OpenAPI for this *path operation* will be:
|
||||
|
||||
The schemas are referenced to another place inside the OpenAPI schema:
|
||||
|
||||
```JSON hl_lines="4 5 6 7 8 9 10 11 12 13 14 15 16"
|
||||
```JSON hl_lines="4-16"
|
||||
{
|
||||
"components": {
|
||||
"schemas": {
|
||||
@@ -168,7 +168,7 @@ You can use this same `responses` parameter to add different media types for the
|
||||
|
||||
For example, you can add an additional media type of `image/png`, declaring that your *path operation* can return a JSON object (with media type `application/json`) or a PNG image:
|
||||
|
||||
```Python hl_lines="17 18 19 20 21 22 23 24 28"
|
||||
```Python hl_lines="19-24 28"
|
||||
{!../../../docs_src/additional_responses/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -192,7 +192,7 @@ For example, you can declare a response with a status code `404` that uses a Pyd
|
||||
|
||||
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
|
||||
|
||||
```Python hl_lines="20 21 22 23 24 25 26 27 28 29 30 31"
|
||||
```Python hl_lines="20-31"
|
||||
{!../../../docs_src/additional_responses/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -228,7 +228,7 @@ You can use that technique to re-use some predefined responses in your *path ope
|
||||
|
||||
For example:
|
||||
|
||||
```Python hl_lines="11 12 13 14 15 24"
|
||||
```Python hl_lines="13-17 26"
|
||||
{!../../../docs_src/additional_responses/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ But you also want it to accept new items. And when the items didn't exist before
|
||||
|
||||
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
|
||||
|
||||
```Python hl_lines="2 19"
|
||||
```Python hl_lines="4 23"
|
||||
{!../../../docs_src/additional_status_codes/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ Later, for your production application, you might want to use a database server
|
||||
* Create a `metadata` object.
|
||||
* Create a table `notes` using the `metadata` object.
|
||||
|
||||
```Python hl_lines="4 14 16 17 18 19 20 21 22"
|
||||
```Python hl_lines="4 14 16-22"
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -38,7 +38,7 @@ Later, for your production application, you might want to use a database server
|
||||
* Create a `DATABASE_URL`.
|
||||
* Create a `database` object.
|
||||
|
||||
```Python hl_lines="3 9 12"
|
||||
```Python hl_lines="3 9 12"
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -54,7 +54,7 @@ Here, this section would run directly, right before starting your **FastAPI** ap
|
||||
* Create an `engine`.
|
||||
* Create all the tables from the `metadata` object.
|
||||
|
||||
```Python hl_lines="25 26 27 28"
|
||||
```Python hl_lines="25-28"
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -65,7 +65,7 @@ Create Pydantic models for:
|
||||
* Notes to be created (`NoteIn`).
|
||||
* Notes to be returned (`Note`).
|
||||
|
||||
```Python hl_lines="31 32 33 36 37 38 39"
|
||||
```Python hl_lines="31-33 36-39"
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -78,7 +78,7 @@ So, you will be able to see it all in the interactive API docs.
|
||||
* Create your `FastAPI` application.
|
||||
* Create event handlers to connect and disconnect from the database.
|
||||
|
||||
```Python hl_lines="42 45 46 47 50 51 52"
|
||||
```Python hl_lines="42 45-47 50-52"
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -86,7 +86,7 @@ So, you will be able to see it all in the interactive API docs.
|
||||
|
||||
Create the *path operation function* to read notes:
|
||||
|
||||
```Python hl_lines="55 56 57 58"
|
||||
```Python hl_lines="55-58"
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -103,7 +103,7 @@ That documents (and validates, serializes, filters) the output data, as a `list`
|
||||
|
||||
Create the *path operation function* to create notes:
|
||||
|
||||
```Python hl_lines="61 62 63 64 65"
|
||||
```Python hl_lines="61-65"
|
||||
{!../../../docs_src/async_sql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
100
docs/en/docs/advanced/async-tests.md
Normal file
@@ -0,0 +1,100 @@
|
||||
# Async Tests
|
||||
|
||||
You have already seen how to test your **FastAPI** applications using the provided `TestClient`, but with it, you can't test or run any other `async` function in your (synchronous) pytest functions.
|
||||
|
||||
Being able to use asynchronous functions in your tests could be useful, for example, when you're querying your database asynchronously. Imagine you want to test sending requests to your FastAPI application and then verify that your backend successfully wrote the correct data in the database, while using an async database library.
|
||||
|
||||
Let's look at how we can make that work.
|
||||
|
||||
## pytest-asyncio
|
||||
|
||||
If we want to call asynchronous functions in our tests, our test functions have to be asynchronous. Pytest provides a neat library for this, called `pytest-asyncio`, that allows us to specify that some test functions are to be called asynchronously.
|
||||
|
||||
You can install it via:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install pytest-asyncio
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## HTTPX
|
||||
|
||||
Even if your **FastAPI** application uses normal `def` functions instead of `async def`, it is still an `async` application underneath.
|
||||
|
||||
The `TestClient` does some magic inside to call the asynchronous FastAPI application in your normal `def` test functions, using standard pytest. But that magic doesn't work anymore when we're using it inside asynchronous functions. By running our tests asynchronously, we can no longer use the `TestClient` inside our test functions.
|
||||
|
||||
Luckily there's a nice alternative, called <a href="https://www.python-httpx.org/" class="external-link" target="_blank">HTTPX</a>.
|
||||
|
||||
HTTPX is an HTTP client for Python 3 that allows us to query our FastAPI application similarly to how we did it with the `TestClient`.
|
||||
|
||||
If you're familiar with the <a href="https://requests.readthedocs.io/en/master/" class="external-link" target="_blank">Requests</a> library, you'll find that the API of HTTPX is almost identical.
|
||||
|
||||
The important difference for us is that with HTTPX we are not limited to synchronous, but can also make asynchronous requests.
|
||||
|
||||
## Example
|
||||
|
||||
For a simple example, let's consider the following `main.py` module:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/async_tests/main.py!}
|
||||
```
|
||||
|
||||
The `test_main.py` module that contains the tests for `main.py` could look like this now:
|
||||
|
||||
```Python
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
```
|
||||
|
||||
## Run it
|
||||
|
||||
You can run your tests as usual via:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pytest
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
## In Detail
|
||||
|
||||
The marker `@pytest.mark.asyncio` tells pytest that this test function should be called asynchronously:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Note that the test function is now `async def` instead of just `def` as before when using the `TestClient`.
|
||||
|
||||
Then we can create an `AsyncClient` with the app, and send async requests to it, using `await`.
|
||||
|
||||
```Python hl_lines="9-10"
|
||||
{!../../../docs_src/async_tests/test_main.py!}
|
||||
```
|
||||
|
||||
This is the equivalent to:
|
||||
|
||||
```Python
|
||||
response = client.get('/')
|
||||
```
|
||||
|
||||
that we used to make our requests with the `TestClient`.
|
||||
|
||||
!!! tip
|
||||
Note that we're using async/await with the new `AsyncClient` - the request is asynchronous.
|
||||
|
||||
## Other Asynchronous Function Calls
|
||||
|
||||
As the testing function is now asynchronous, you can now also call (and `await`) other `async` functions apart from sending requests to your FastAPI application in your tests, exactly as you would call them anywhere else in your code.
|
||||
|
||||
!!! tip
|
||||
If you encounter a `RuntimeError: Task attached to a different loop` when integrating asynchronous function calls in your tests (e.g. when using <a href="https://stackoverflow.com/questions/41584243/runtimeerror-task-attached-to-a-different-loop" class="external-link" target="_blank">MongoDB's MotorClient</a>) check out <a href="https://github.com/pytest-dev/pytest-asyncio/issues/38#issuecomment-264418154" class="external-link" target="_blank">this issue</a> in the pytest-asyncio repository.
|
||||
@@ -42,16 +42,19 @@ proxy --> server
|
||||
!!! tip
|
||||
The IP `0.0.0.0` is commonly used to mean that the program listens on all the IPs available in that machine/server.
|
||||
|
||||
The docs UI would also need that the JSON payload with the OpenAPI schema has the path defined as `/api/v1/app` (behind the proxy) instead of `/app`. For example, something like:
|
||||
The docs UI would also need the OpenAPI schema to declare that this API `server` is located at `/api/v1` (behind the proxy). For example:
|
||||
|
||||
```JSON hl_lines="5"
|
||||
```JSON hl_lines="4-8"
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
// More stuff here
|
||||
"paths": {
|
||||
"/api/v1/app": {
|
||||
// More stuff here
|
||||
"servers": [
|
||||
{
|
||||
"url": "/api/v1"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
// More stuff here
|
||||
}
|
||||
}
|
||||
```
|
||||
@@ -235,7 +238,7 @@ Now, if you go to the URL with the port for Uvicorn: <a href="http://127.0.0.1:8
|
||||
!!! tip
|
||||
Notice that even though you are accessing it at `http://127.0.0.1:8000/app` it shows the `root_path` of `/api/v1`, taken from the option `--root-path`.
|
||||
|
||||
And now open the URL with the port for Traefik, including the path prefix: <a href="http://127.0.0.1:9999/api/v1/app" class="external-link" target="_blank">http://127.0.0.1:9999/api/vi/app</a>.
|
||||
And now open the URL with the port for Traefik, including the path prefix: <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>.
|
||||
|
||||
We get the same response:
|
||||
|
||||
@@ -264,16 +267,78 @@ You can check it at <a href="http://127.0.0.1:8000/docs" class="external-link" t
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image01.png">
|
||||
|
||||
But if we access the docs UI at the "official" URL using the proxy, at `/api/v1/docs`, it works correctly! 🎉
|
||||
|
||||
Right as we wanted it. ✔️
|
||||
|
||||
This is because FastAPI uses this `root_path` internally to tell the docs UI to use the URL for OpenAPI with the path prefix provided by `root_path`.
|
||||
But if we access the docs UI at the "official" URL using the proxy with port `9999`, at `/api/v1/docs`, it works correctly! 🎉
|
||||
|
||||
You can check it at <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>:
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image02.png">
|
||||
|
||||
Right as we wanted it. ✔️
|
||||
|
||||
This is because FastAPI uses this `root_path` to create the default `server` in OpenAPI with the URL provided by `root_path`.
|
||||
|
||||
## Additional servers
|
||||
|
||||
!!! warning
|
||||
This is a more advanced use case. Feel free to skip it.
|
||||
|
||||
By default, **FastAPI** will create a `server` in the OpenAPI schema with the URL for the `root_path`.
|
||||
|
||||
But you can also provide other alternative `servers`, for example if you want *the same* docs UI to interact with a staging and production environments.
|
||||
|
||||
If you pass a custom list of `servers` and there's a `root_path` (because your API lives behind a proxy), **FastAPI** will insert a "server" with this `root_path` at the beginning of the list.
|
||||
|
||||
For example:
|
||||
|
||||
```Python hl_lines="4-7"
|
||||
{!../../../docs_src/behind_a_proxy/tutorial003.py!}
|
||||
```
|
||||
|
||||
Will generate an OpenAPI schema like:
|
||||
|
||||
```JSON hl_lines="5-7"
|
||||
{
|
||||
"openapi": "3.0.2",
|
||||
// More stuff here
|
||||
"servers": [
|
||||
{
|
||||
"url": "/api/v1"
|
||||
},
|
||||
{
|
||||
"url": "https://stag.example.com",
|
||||
"description": "Staging environment"
|
||||
},
|
||||
{
|
||||
"url": "https://prod.example.com",
|
||||
"description": "Production environment"
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
// More stuff here
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
Notice the auto-generated server with a `url` value of `/api/v1`, taken from the `root_path`.
|
||||
|
||||
In the docs UI at <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> it would look like:
|
||||
|
||||
<img src="/img/tutorial/behind-a-proxy/image03.png">
|
||||
|
||||
!!! tip
|
||||
The docs UI will interact with the server that you select.
|
||||
|
||||
### Disable automatic server from `root_path`
|
||||
|
||||
If you don't want **FastAPI** to include an automatic server using the `root_path`, you can use the parameter `root_path_in_servers=False`:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/behind_a_proxy/tutorial004.py!}
|
||||
```
|
||||
|
||||
and then it won't include it in the OpenAPI schema.
|
||||
|
||||
## Mounting a sub-application
|
||||
|
||||
If you need to mount a sub-application (as described in [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}) while also using a proxy with `root_path`, you can do it normally, as you would expect.
|
||||
|
||||
@@ -36,7 +36,7 @@ If there's no `gzip` in the header, it will not try to decompress the body.
|
||||
|
||||
That way, the same route class can handle gzip compressed or uncompressed requests.
|
||||
|
||||
```Python hl_lines="8 9 10 11 12 13 14 15"
|
||||
```Python hl_lines="8-15"
|
||||
{!../../../docs_src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -50,7 +50,7 @@ This method returns a function. And that function is what will receive a request
|
||||
|
||||
Here we use it to create a `GzipRequest` from the original request.
|
||||
|
||||
```Python hl_lines="18 19 20 21 22 23 24 25 26"
|
||||
```Python hl_lines="18-26"
|
||||
{!../../../docs_src/custom_request_and_route/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -84,13 +84,13 @@ We can also use this same approach to access the request body in an exception ha
|
||||
|
||||
All we need to do is handle the request inside a `try`/`except` block:
|
||||
|
||||
```Python hl_lines="13 15"
|
||||
```Python hl_lines="13 15"
|
||||
{!../../../docs_src/custom_request_and_route/tutorial002.py!}
|
||||
```
|
||||
|
||||
If an exception occurs, the`Request` instance will still be in scope, so we can read and make use of the request body when handling the error:
|
||||
|
||||
```Python hl_lines="16 17 18"
|
||||
```Python hl_lines="16-18"
|
||||
{!../../../docs_src/custom_request_and_route/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -104,6 +104,6 @@ You can also set the `route_class` parameter of an `APIRouter`:
|
||||
|
||||
In this example, the *path operations* under the `router` will use the custom `TimedRoute` class, and will have an extra `X-Response-Time` header in the response with the time it took to generate the response:
|
||||
|
||||
```Python hl_lines="13 14 15 16 17 18 19 20"
|
||||
```Python hl_lines="13-20"
|
||||
{!../../../docs_src/custom_request_and_route/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -21,7 +21,7 @@ For example, if you are squeezing performance, you can install and use <a href="
|
||||
|
||||
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
|
||||
|
||||
```Python hl_lines="2 7"
|
||||
```Python hl_lines="2 7"
|
||||
{!../../../docs_src/custom_response/tutorial001b.py!}
|
||||
```
|
||||
|
||||
@@ -42,7 +42,7 @@ 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"
|
||||
```Python hl_lines="2 7"
|
||||
{!../../../docs_src/custom_response/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -59,7 +59,7 @@ As seen in [Return a Response directly](response-directly.md){.internal-link tar
|
||||
|
||||
The same example from above, returning an `HTMLResponse`, could look like:
|
||||
|
||||
```Python hl_lines="2 7 19"
|
||||
```Python hl_lines="2 7 19"
|
||||
{!../../../docs_src/custom_response/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -79,7 +79,7 @@ The `response_class` will then be used only to document the OpenAPI *path operat
|
||||
|
||||
For example, it could be something like:
|
||||
|
||||
```Python hl_lines="7 23 21"
|
||||
```Python hl_lines="7 21 23"
|
||||
{!../../../docs_src/custom_response/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -150,7 +150,7 @@ An alternative JSON response using <a href="https://github.com/ultrajson/ultrajs
|
||||
!!! warning
|
||||
`ujson` is less careful than Python's built-in implementation in how it handles some edge-cases.
|
||||
|
||||
```Python hl_lines="2 7"
|
||||
```Python hl_lines="2 7"
|
||||
{!../../../docs_src/custom_response/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -179,7 +179,7 @@ If you have a file-like object (e.g. the object returned by `open()`), you can r
|
||||
|
||||
This includes many libraries to interact with cloud storage, video processing, and others.
|
||||
|
||||
```Python hl_lines="2 10 11"
|
||||
```Python hl_lines="2 10-11"
|
||||
{!../../../docs_src/custom_response/tutorial008.py!}
|
||||
```
|
||||
|
||||
@@ -211,7 +211,7 @@ The parameter that defines this is `default_response_class`.
|
||||
|
||||
In the example below, **FastAPI** will use `ORJSONResponse` by default, in all *path operations*, instead of `JSONResponse`.
|
||||
|
||||
```Python hl_lines="2 4"
|
||||
```Python hl_lines="2 4"
|
||||
{!../../../docs_src/custom_response/tutorial010.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,6 +4,9 @@ You can define event handlers (functions) that need to be executed before the ap
|
||||
|
||||
These functions can be declared with `async def` or normal `def`.
|
||||
|
||||
!!! warning
|
||||
Only event handlers for the main application will be executed, not for [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}.
|
||||
|
||||
## `startup` event
|
||||
|
||||
To add a function that should be run before the application starts, declare it with the event `"startup"`:
|
||||
@@ -41,4 +44,4 @@ Here, the `shutdown` event handler function will write a text line `"Application
|
||||
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>.
|
||||
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>.
|
||||
|
||||
@@ -32,7 +32,6 @@ And that function `get_openapi()` receives as parameters:
|
||||
* `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
|
||||
|
||||
@@ -44,7 +43,7 @@ For example, let's add <a href="https://github.com/Rebilly/ReDoc/blob/master/doc
|
||||
|
||||
First, write all your **FastAPI** application as normally:
|
||||
|
||||
```Python hl_lines="1 4 7 8 9"
|
||||
```Python hl_lines="1 4 7-9"
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -52,22 +51,15 @@ First, write all your **FastAPI** application as normally:
|
||||
|
||||
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 21"
|
||||
```Python hl_lines="2 15-20"
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The `openapi_prefix` will contain any prefix needed for the generated OpenAPI *path operations*.
|
||||
|
||||
FastAPI will automatically use the `root_path` to pass it in the `openapi_prefix`.
|
||||
|
||||
But the important thing is that your function should receive that parameter `openapi_prefix` and pass it along.
|
||||
|
||||
### Modify the OpenAPI schema
|
||||
|
||||
Now you can add the ReDoc extension, adding a custom `x-logo` to the `info` "object" in the OpenAPI schema:
|
||||
|
||||
```Python hl_lines="22 23 24"
|
||||
```Python hl_lines="21-23"
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -79,7 +71,7 @@ That way, your application won't have to generate the schema every time a user o
|
||||
|
||||
It will be generated only once, and then the same cached schema will be used for the next requests.
|
||||
|
||||
```Python hl_lines="13 14 25 26"
|
||||
```Python hl_lines="13-14 24-25"
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -87,7 +79,7 @@ It will be generated only once, and then the same cached schema will be used for
|
||||
|
||||
Now you can replace the `.openapi()` method with your new function.
|
||||
|
||||
```Python hl_lines="29"
|
||||
```Python hl_lines="28"
|
||||
{!../../../docs_src/extending_openapi/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -180,7 +172,7 @@ $ pip install aiofiles
|
||||
* Import `StaticFiles`.
|
||||
* "Mount" a `StaticFiles()` instance in a specific path.
|
||||
|
||||
```Python hl_lines="7 11"
|
||||
```Python hl_lines="7 11"
|
||||
{!../../../docs_src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -232,7 +224,7 @@ You can re-use FastAPI's internal functions to create the HTML pages for the doc
|
||||
|
||||
And similarly for ReDoc...
|
||||
|
||||
```Python hl_lines="2 3 4 5 6 14 15 16 17 18 19 20 21 22 25 26 27 30 31 32 33 34 35 36"
|
||||
```Python hl_lines="2-6 14-22 25-27 30-36"
|
||||
{!../../../docs_src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -247,7 +239,7 @@ And similarly for ReDoc...
|
||||
|
||||
Now, to be able to test that everything works, create a *path operation*:
|
||||
|
||||
```Python hl_lines="39 40 41"
|
||||
```Python hl_lines="39-41"
|
||||
{!../../../docs_src/extending_openapi/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ GraphQL is implemented with Graphene, you can check <a href="https://docs.graphe
|
||||
|
||||
Import `graphene` and define your GraphQL data:
|
||||
|
||||
```Python hl_lines="1 6 7 8 9 10"
|
||||
```Python hl_lines="1 6-10"
|
||||
{!../../../docs_src/graphql/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -18,7 +18,7 @@ Import `graphene` and define your GraphQL data:
|
||||
|
||||
Then import and add Starlette's `GraphQLApp`:
|
||||
|
||||
```Python hl_lines="3 14"
|
||||
```Python hl_lines="3 14"
|
||||
{!../../../docs_src/graphql/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -16,3 +16,9 @@ In the next sections you will see other options, configurations, and additional
|
||||
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.
|
||||
|
||||
## TestDriven.io course
|
||||
|
||||
If you would like to take an advanced-beginner course to complement this section of the docs, you might want to check: <a href="https://testdriven.io/courses/tdd-fastapi/" class="external-link" target="_blank">Test-Driven Development with FastAPI and Docker</a> by **TestDriven.io**.
|
||||
|
||||
They are currently donating 10% of all profits to the development of **FastAPI**. 🎉 😄
|
||||
|
||||
@@ -62,7 +62,7 @@ Any incoming requests to `http` or `ws` will be redirected to the secure scheme
|
||||
|
||||
Enforces that all incoming requests have a correctly set `Host` header, in order to guard against HTTP Host Header attacks.
|
||||
|
||||
```Python hl_lines="2 6 7 8"
|
||||
```Python hl_lines="2 6-8"
|
||||
{!../../../docs_src/advanced_middleware/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ You can adapt it to any other NoSQL database like:
|
||||
|
||||
For now, don't pay attention to the rest, only the imports:
|
||||
|
||||
```Python hl_lines="6 7 8"
|
||||
```Python hl_lines="3-5"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -29,7 +29,7 @@ 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"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -54,7 +54,7 @@ This utility function will:
|
||||
* Set defaults for timeouts.
|
||||
* Return it.
|
||||
|
||||
```Python hl_lines="13 14 15 16 17 18 19 20 21 22"
|
||||
```Python hl_lines="12-21"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -66,7 +66,7 @@ As **Couchbase** "documents" are actually just "JSON objects", we can model them
|
||||
|
||||
First, let's create a `User` model:
|
||||
|
||||
```Python hl_lines="25 26 27 28 29"
|
||||
```Python hl_lines="24-28"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -80,7 +80,7 @@ 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"
|
||||
```Python hl_lines="31-33"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -100,7 +100,7 @@ Now create a function that will:
|
||||
|
||||
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"
|
||||
```Python hl_lines="36-42"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -135,7 +135,7 @@ UserInDB(username="johndoe", hashed_password="some_hash")
|
||||
|
||||
### Create the `FastAPI` app
|
||||
|
||||
```Python hl_lines="47"
|
||||
```Python hl_lines="46"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -143,9 +143,9 @@ UserInDB(username="johndoe", hashed_password="some_hash")
|
||||
|
||||
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:
|
||||
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 just get the bucket directly and pass it to our utility functions:
|
||||
|
||||
```Python hl_lines="50 51 52 53 54"
|
||||
```Python hl_lines="49-53"
|
||||
{!../../../docs_src/nosql_databases/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ It will have a *path operation* that will receive an `Invoice` body, and a query
|
||||
|
||||
This part is pretty normal, most of the code is probably already familiar to you:
|
||||
|
||||
```Python hl_lines="8 9 10 11 12 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53"
|
||||
```Python hl_lines="10-14 37-54"
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -92,7 +92,7 @@ Because of that, you need to declare what will be the `default_response_class`,
|
||||
|
||||
But as we are never calling `app.include_router(some_router)`, we need to set the `default_response_class` during creation of the `APIRouter`.
|
||||
|
||||
```Python hl_lines="3 24"
|
||||
```Python hl_lines="5 26"
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -105,7 +105,7 @@ It should look just like a normal FastAPI *path operation*:
|
||||
* It should probably have a declaration of the body it should receive, e.g. `body: InvoiceEvent`.
|
||||
* And it could also have a declaration of the response it should return, e.g. `response_model=InvoiceEventReceived`.
|
||||
|
||||
```Python hl_lines="15 16 17 20 21 27 28 29 30 31"
|
||||
```Python hl_lines="17-19 22-23 29-33"
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -172,7 +172,7 @@ At this point you have the *callback path operation(s)* needed (the one(s) that
|
||||
|
||||
Now use the parameter `callbacks` in *your API's path operation decorator* to pass the attribute `.routes` (that's actually just a `list` of routes/*path operations*) from that callback router:
|
||||
|
||||
```Python hl_lines="34"
|
||||
```Python hl_lines="36"
|
||||
{!../../../docs_src/openapi_callbacks/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ If you want to use your APIs' function names as `operationId`s, you can iterate
|
||||
|
||||
You should do it after adding all your *path operations*.
|
||||
|
||||
```Python hl_lines="2 12 13 14 15 16 17 18 19 20 21 24"
|
||||
```Python hl_lines="2 12-21 24"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -47,6 +47,6 @@ Adding an `\f` (an escaped "form feed" character) causes **FastAPI** to truncate
|
||||
|
||||
It won't show up in the documentation, but other tools (such as Sphinx) will be able to use the rest.
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25 26 27 28 29"
|
||||
```Python hl_lines="19-29"
|
||||
{!../../../docs_src/path_operation_advanced_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -6,7 +6,7 @@ You can declare a parameter of type `Response` in your *path operation function*
|
||||
|
||||
And then you can set cookies in that *temporal* response object.
|
||||
|
||||
```Python hl_lines="1 8 9"
|
||||
```Python hl_lines="1 8-9"
|
||||
{!../../../docs_src/response_cookies/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -26,7 +26,7 @@ To do that, you can create a response as described in [Return a Response Directl
|
||||
|
||||
Then set Cookies in it, and then return it:
|
||||
|
||||
```Python hl_lines="10 11 12"
|
||||
```Python hl_lines="10-12"
|
||||
{!../../../docs_src/response_cookies/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ For example, you cannot put a Pydantic model in a `JSONResponse` without first c
|
||||
|
||||
For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response:
|
||||
|
||||
```Python hl_lines="4 6 20 21"
|
||||
```Python hl_lines="6-7 21-22"
|
||||
{!../../../docs_src/response_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ 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="1 7 8"
|
||||
```Python hl_lines="1 7-8"
|
||||
{!../../../docs_src/response_headers/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -24,7 +24,7 @@ You can also add headers when you return a `Response` directly.
|
||||
|
||||
Create a response as described in [Return a Response Directly](response-directly.md){.internal-link target=_blank} and pass the headers as an additional parameter:
|
||||
|
||||
```Python hl_lines="10 11 12"
|
||||
```Python hl_lines="10-12"
|
||||
{!../../../docs_src/response_headers/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ Then, when you type that username and password, the browser sends them in the he
|
||||
* It returns an object of type `HTTPBasicCredentials`:
|
||||
* It contains the `username` and `password` sent.
|
||||
|
||||
```Python hl_lines="2 6 10"
|
||||
```Python hl_lines="2 6 10"
|
||||
{!../../../docs_src/security/tutorial006.py!}
|
||||
```
|
||||
|
||||
@@ -36,7 +36,7 @@ Use a dependency to check if the username and password are correct.
|
||||
|
||||
For this, use the Python standard module <a href="https://docs.python.org/3/library/secrets.html" class="external-link" target="_blank">`secrets`</a> to check the username and password:
|
||||
|
||||
```Python hl_lines="1 11 12 13"
|
||||
```Python hl_lines="1 11-13"
|
||||
{!../../../docs_src/security/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -54,9 +54,9 @@ But by using the `secrets.compare_digest()` it will be secure against a type of
|
||||
|
||||
But what's a "timing attack"?
|
||||
|
||||
Let's imagine an attacker is trying to guess the username and password.
|
||||
Let's imagine some attackers are trying to guess the username and password.
|
||||
|
||||
And that attacker sends a request with a username `johndoe` and a password `love123`.
|
||||
And they send a request with a username `johndoe` and a password `love123`.
|
||||
|
||||
Then the Python code in your application would be equivalent to something like:
|
||||
|
||||
@@ -67,7 +67,7 @@ if "johndoe" == "stanleyjobson" and "love123" == "swordfish":
|
||||
|
||||
But right at the moment Python compares the first `j` in `johndoe` to the first `s` in `stanleyjobson`, it will return `False`, because it already knows that those two strings are not the same, thinking that "there's no need to waste more computation comparing the rest of the letters". And your application will say "incorrect user or password".
|
||||
|
||||
But then the attacker tries with username `stanleyjobsox` and password `love123`.
|
||||
But then the attackers try with username `stanleyjobsox` and password `love123`.
|
||||
|
||||
And your application code does something like:
|
||||
|
||||
@@ -78,17 +78,17 @@ if "stanleyjobsox" == "stanleyjobson" and "love123" == "swordfish":
|
||||
|
||||
Python will have to compare the whole `stanleyjobso` in both `stanleyjobsox` and `stanleyjobson` before realizing that both strings are not the same. So it will take some extra microseconds to reply back "incorrect user or password".
|
||||
|
||||
#### The time to answer helps the attacker
|
||||
#### The time to answer helps the attackers
|
||||
|
||||
At that point, by noticing that the server took some microseconds longer to send the "incorrect user or password" response, the attacker will know that she/he got _something_ right, some of the initial letters were right.
|
||||
At that point, by noticing that the server took some microseconds longer to send the "incorrect user or password" response, the attackers will know that they got _something_ right, some of the initial letters were right.
|
||||
|
||||
And then she/he can try again knowing that it's probably something more similar to `stanleyjobsox` than to `johndoe`.
|
||||
And then they can try again knowing that it's probably something more similar to `stanleyjobsox` than to `johndoe`.
|
||||
|
||||
#### A "professional" attack
|
||||
|
||||
Of course, the attacker would not try all this by hand, she/he would write a program to do it, possibly with thousands or millions of tests per second. And would get just one extra correct letter at a time.
|
||||
Of course, the attackers would not try all this by hand, they would write a program to do it, possibly with thousands or millions of tests per second. And would get just one extra correct letter at a time.
|
||||
|
||||
But doing that, in some minutes or hours the attacker would have guessed the correct username and password, with the "help" of our application, just using the time taken to answer.
|
||||
But doing that, in some minutes or hours the attackers would have guessed the correct username and password, with the "help" of our application, just using the time taken to answer.
|
||||
|
||||
#### Fix it with `secrets.compare_digest()`
|
||||
|
||||
@@ -102,6 +102,6 @@ That way, using `secrets.compare_digest()` in your application code, it will be
|
||||
|
||||
After detecting that the credentials are incorrect, return an `HTTPException` with a status code 401 (the same returned when no credentials are provided) and add the header `WWW-Authenticate` to make the browser show the login prompt again:
|
||||
|
||||
```Python hl_lines="15 16 17 18 19"
|
||||
```Python hl_lines="15-19"
|
||||
{!../../../docs_src/security/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -56,7 +56,7 @@ They are normally used to declare specific security permissions, for example:
|
||||
|
||||
First, let's quickly see the parts that change from the examples in the main **Tutorial - User Guide** for [OAuth2 with Password (and hashing), Bearer with JWT tokens](../../tutorial/security/oauth2-jwt.md){.internal-link target=_blank}. Now using OAuth2 scopes:
|
||||
|
||||
```Python hl_lines="2 5 9 13 47 65 106 108 109 110 111 112 113 114 115 116 122 123 124 125 129 130 131 132 133 134 135 140 154"
|
||||
```Python hl_lines="2 4 8 12 46 64 105 107-115 121-124 128-134 139 153"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -68,7 +68,7 @@ The first change is that now we are declaring the OAuth2 security scheme with tw
|
||||
|
||||
The `scopes` parameter receives a `dict` with each scope as a key and the description as the value:
|
||||
|
||||
```Python hl_lines="63 64 65 66"
|
||||
```Python hl_lines="62-65"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -93,7 +93,7 @@ And we return the scopes as part of the JWT token.
|
||||
|
||||
But in your application, for security, you should make sure you only add the scopes that the user is actually able to have, or the ones you have predefined.
|
||||
|
||||
```Python hl_lines="155"
|
||||
```Python hl_lines="153"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -118,7 +118,7 @@ In this case, it requires the scope `me` (it could require more than one scope).
|
||||
|
||||
We are doing it here to demonstrate how **FastAPI** handles scopes declared at different levels.
|
||||
|
||||
```Python hl_lines="5 140 167"
|
||||
```Python hl_lines="4 139 166"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -143,7 +143,7 @@ We also declare a special parameter of type `SecurityScopes`, imported from `fas
|
||||
|
||||
This `SecurityScopes` class is similar to `Request` (`Request` was used to get the request object directly).
|
||||
|
||||
```Python hl_lines="9 106"
|
||||
```Python hl_lines="8 105"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -159,7 +159,7 @@ We create an `HTTPException` that we can re-use (`raise`) later at several point
|
||||
|
||||
In this exception, we include the scopes required (if any) as a string separated by spaces (using `scope_str`). We put that string containing the scopes in in the `WWW-Authenticate` header (this is part of the spec).
|
||||
|
||||
```Python hl_lines="106 108 109 110 111 112 113 114 115 116"
|
||||
```Python hl_lines="105 107-115"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -177,7 +177,7 @@ Instead of, for example, a `dict`, or something else, as it could break the appl
|
||||
|
||||
We also verify that we have a user with that username, and if not, we raise that same exception we created before.
|
||||
|
||||
```Python hl_lines="47 117 118 119 120 121 122 123 124 125 126 127 128"
|
||||
```Python hl_lines="46 116-127"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -187,7 +187,7 @@ We now verify that all the scopes required, by this dependency and all the depen
|
||||
|
||||
For this, we use `security_scopes.scopes`, that contains a `list` with all these scopes as `str`.
|
||||
|
||||
```Python hl_lines="129 130 131 132 133 134 135"
|
||||
```Python hl_lines="128-134"
|
||||
{!../../../docs_src/security/tutorial005.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -135,7 +135,7 @@ The same way as with Pydantic models, you declare class attributes with type ann
|
||||
|
||||
You can use all the same validation features and tools you use for Pydantic models, like different data types and additional validations with `Field()`.
|
||||
|
||||
```Python hl_lines="2 5 6 7 8 11"
|
||||
```Python hl_lines="2 5-8 11"
|
||||
{!../../../docs_src/settings/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -150,7 +150,7 @@ Next it will convert and validate the data. So, when you use that `settings` obj
|
||||
|
||||
Then you can use the new `settings` object in your application:
|
||||
|
||||
```Python hl_lines="18 19 20"
|
||||
```Python hl_lines="18-20"
|
||||
{!../../../docs_src/settings/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -189,7 +189,7 @@ For example, you could have a file `config.py` with:
|
||||
|
||||
And then use it in a file `main.py`:
|
||||
|
||||
```Python hl_lines="3 11 12 13"
|
||||
```Python hl_lines="3 11-13"
|
||||
{!../../../docs_src/settings/app01/main.py!}
|
||||
```
|
||||
|
||||
@@ -216,7 +216,7 @@ Notice that now we don't create a default instance `settings = Settings()`.
|
||||
|
||||
Now we create a dependency that returns a new `config.Settings()`.
|
||||
|
||||
```Python hl_lines="5 11 12"
|
||||
```Python hl_lines="5 11-12"
|
||||
{!../../../docs_src/settings/app02/main.py!}
|
||||
```
|
||||
|
||||
@@ -227,7 +227,7 @@ Now we create a dependency that returns a new `config.Settings()`.
|
||||
|
||||
And then we can require it from the *path operation function* as a dependency and use it anywhere we need it.
|
||||
|
||||
```Python hl_lines="16 18 19 20"
|
||||
```Python hl_lines="16 18-20"
|
||||
{!../../../docs_src/settings/app02/main.py!}
|
||||
```
|
||||
|
||||
@@ -235,7 +235,7 @@ And then we can require it from the *path operation function* as a dependency an
|
||||
|
||||
Then it would be very easy to provide a different settings object during testing by creating a dependency override for `get_settings`:
|
||||
|
||||
```Python hl_lines="8 9 12 21"
|
||||
```Python hl_lines="8-9 12 21"
|
||||
{!../../../docs_src/settings/app02/test_main.py!}
|
||||
```
|
||||
|
||||
@@ -272,7 +272,7 @@ APP_NAME="ChimichangApp"
|
||||
|
||||
And then update your `config.py` with:
|
||||
|
||||
```Python hl_lines="9 10"
|
||||
```Python hl_lines="9-10"
|
||||
{!../../../docs_src/settings/app03/config.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -113,7 +113,7 @@ This might seem a bit complex (and it actually is), you don't really need to com
|
||||
|
||||
We will create a `PeeweeConnectionState`:
|
||||
|
||||
```Python hl_lines="10 11 12 13 14 15 16 17 18 19"
|
||||
```Python hl_lines="10-19"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/database.py!}
|
||||
```
|
||||
|
||||
@@ -161,7 +161,7 @@ This is the same you would do if you followed the Peewee tutorial and updated th
|
||||
|
||||
Import `db` from `database` (the file `database.py` from above) and use it here.
|
||||
|
||||
```Python hl_lines="3 6 7 8 9 10 11 12 15 16 17 18 19 20 21"
|
||||
```Python hl_lines="3 6-12 15-21"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/models.py!}
|
||||
```
|
||||
|
||||
@@ -189,7 +189,7 @@ Now let's check the file `sql_app/schemas.py`.
|
||||
|
||||
Create all the same Pydantic models as in the SQLAlchemy tutorial:
|
||||
|
||||
```Python hl_lines="16 17 18 21 22 25 26 27 28 29 30 34 35 38 39 42 43 44 45 46 47 48"
|
||||
```Python hl_lines="16-18 21-22 25-30 34-35 38-39 42-48"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
@@ -214,7 +214,7 @@ But recent versions of Pydantic allow providing a custom class that inherits fro
|
||||
|
||||
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"
|
||||
```Python hl_lines="3 8-13 31 49"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/schemas.py!}
|
||||
```
|
||||
|
||||
@@ -235,7 +235,7 @@ Now let's see the file `sql_app/crud.py`.
|
||||
|
||||
Create all the same CRUD utils as in the SQLAlchemy tutorial, all the code is very similar:
|
||||
|
||||
```Python hl_lines="1 4 5 8 9 12 13 16 17 18 19 20 23 24 27 28 29 30"
|
||||
```Python hl_lines="1 4-5 8-9 12-13 16-20 23-24 27-30"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/crud.py!}
|
||||
```
|
||||
|
||||
@@ -259,7 +259,7 @@ And now in the file `sql_app/main.py` let's integrate and use all the other part
|
||||
|
||||
In a very simplistic way create the database tables:
|
||||
|
||||
```Python hl_lines="9 10 11"
|
||||
```Python hl_lines="9-11"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -267,7 +267,7 @@ In a very simplistic way create the database tables:
|
||||
|
||||
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"
|
||||
```Python hl_lines="23-29"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -291,7 +291,7 @@ For all the `contextvars` parts to work, we need to make sure we have an indepen
|
||||
|
||||
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"
|
||||
```Python hl_lines="18-20"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
@@ -310,7 +310,7 @@ If you are using a <a href="http://docs.peewee-orm.com/en/latest/peewee/database
|
||||
|
||||
So, you would reset it with:
|
||||
|
||||
```Python hl_lines="3 4"
|
||||
```Python hl_lines="3-4"
|
||||
async def reset_db_state():
|
||||
database.db.obj._state._state.set(db_state_default.copy())
|
||||
database.db.obj._state.reset()
|
||||
@@ -320,7 +320,7 @@ async def reset_db_state():
|
||||
|
||||
Now, finally, here's the standard **FastAPI** *path operations* code.
|
||||
|
||||
```Python hl_lines="32 33 34 35 36 37 40 41 42 43 46 47 48 49 50 51 52 53 56 57 58 59 60 61 62 65 66 67 68 71 72 73 74 75 76 77 78 79"
|
||||
```Python hl_lines="32-37 40-43 46-53 56-62 65-68 71-79"
|
||||
{!../../../docs_src/sql_databases_peewee/sql_app/main.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ If you need to have two independent FastAPI applications, with their own indepen
|
||||
|
||||
## Mounting a **FastAPI** application
|
||||
|
||||
"Mounting" means adding a completely "independent" application in a specific path, that then takes care of handling all everything under that path, with the _path operations_ declared in that sub-application.
|
||||
"Mounting" means adding a completely "independent" application in a specific path, that then takes care of handling everything under that path, with the _path operations_ declared in that sub-application.
|
||||
|
||||
### Top-level application
|
||||
|
||||
First, create the main, top-level, **FastAPI** application, and its *path operations*:
|
||||
|
||||
```Python hl_lines="3 6 7 8"
|
||||
```Python hl_lines="3 6-8"
|
||||
{!../../../docs_src/sub_applications/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -20,7 +20,7 @@ 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":
|
||||
|
||||
```Python hl_lines="11 14 15 16"
|
||||
```Python hl_lines="11 14-16"
|
||||
{!../../../docs_src/sub_applications/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ In your top-level application, `app`, mount the sub-application, `subapi`.
|
||||
|
||||
In this case, it will be mounted at the path `/subapi`:
|
||||
|
||||
```Python hl_lines="11 19"
|
||||
```Python hl_lines="11 19"
|
||||
{!../../../docs_src/sub_applications/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -39,13 +39,16 @@ $ pip install aiofiles
|
||||
* 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="3 10 14 15"
|
||||
```Python hl_lines="4 11 15-16"
|
||||
{!../../../docs_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*.
|
||||
|
||||
!!! tip
|
||||
By declaring `response_class=HTMLResponse` the docs UI will be able to know that the response will be HTML.
|
||||
|
||||
!!! note "Technical Details"
|
||||
You could also use `from starlette.templating import Jinja2Templates`.
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ We create a new file at `sql_app/tests/test_sql_app.py`.
|
||||
|
||||
So the new file structure looks like:
|
||||
|
||||
``` hl_lines="9 10 11"
|
||||
``` hl_lines="9-11"
|
||||
.
|
||||
└── sql_app
|
||||
├── __init__.py
|
||||
@@ -48,7 +48,7 @@ For the tests we'll use a file `test.db` instead of `sql_app.db`.
|
||||
|
||||
But the rest of the session code is more or less the same, we just copy it.
|
||||
|
||||
```Python hl_lines="8 9 10 11 12 13"
|
||||
```Python hl_lines="8-13"
|
||||
{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!}
|
||||
```
|
||||
|
||||
@@ -77,7 +77,7 @@ So we add that line here, with the new file.
|
||||
|
||||
Now we create the dependency override and add it to the overrides for our app.
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 27"
|
||||
```Python hl_lines="19-24 27"
|
||||
{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!}
|
||||
```
|
||||
|
||||
@@ -88,7 +88,7 @@ Now we create the dependency override and add it to the overrides for our app.
|
||||
|
||||
Then we can just test the app as normally.
|
||||
|
||||
```Python hl_lines="32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47"
|
||||
```Python hl_lines="32-47"
|
||||
{!../../../docs_src/sql_databases/sql_app/tests/test_sql_app.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ To override a dependency for testing, you put as a key the original dependency (
|
||||
|
||||
And then **FastAPI** will call that override instead of the original dependency.
|
||||
|
||||
```Python hl_lines="24 25 28"
|
||||
```Python hl_lines="26-27 30"
|
||||
{!../../../docs_src/dependency_testing/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
When you need your event handlers (`startup` and `shutdown`) to run in your tests, you can use the `TestClient` with a `with` statement:
|
||||
|
||||
```Python hl_lines="9 10 11 12 20 21 22 23 24"
|
||||
```Python hl_lines="9-12 20-24"
|
||||
{!../../../docs_src/app_testing/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -4,6 +4,9 @@ You can use the same `TestClient` to test WebSockets.
|
||||
|
||||
For this, you use the `TestClient` in a `with` statement, connecting to the WebSocket:
|
||||
|
||||
```Python hl_lines="27 28 29 30 31"
|
||||
```Python hl_lines="27-31"
|
||||
{!../../../docs_src/app_testing/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! note
|
||||
For more details, check Starlette's documentation for <a href="https://www.starlette.io/testclient/#testing-websocket-sessions" class="external-link" target="_blank">testing WebSockets</a>.
|
||||
|
||||
@@ -29,7 +29,7 @@ Let's imagine you want to get the client's IP address/host inside of your *path
|
||||
|
||||
For that you need to access the request directly.
|
||||
|
||||
```Python hl_lines="1 7 8"
|
||||
```Python hl_lines="1 7-8"
|
||||
{!../../../docs_src/using_request_directly/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ 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:
|
||||
|
||||
```Python hl_lines="2 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 41 42 43"
|
||||
```Python hl_lines="2 6-38 41-43"
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -32,7 +32,7 @@ But it's the simplest way to focus on the server-side of WebSockets and have a w
|
||||
|
||||
In your **FastAPI** application, create a `websocket`:
|
||||
|
||||
```Python hl_lines="1 46 47"
|
||||
```Python hl_lines="1 46-47"
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -45,7 +45,7 @@ In your **FastAPI** application, create a `websocket`:
|
||||
|
||||
In your WebSocket route you can `await` for messages and send messages.
|
||||
|
||||
```Python hl_lines="48 49 50 51 52"
|
||||
```Python hl_lines="48-52"
|
||||
{!../../../docs_src/websockets/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -98,7 +98,7 @@ In WebSocket endpoints you can import from `fastapi` and use:
|
||||
|
||||
They work the same way as for other FastAPI endpoints/*path operations*:
|
||||
|
||||
```Python hl_lines="56 57 58 59 60 61 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79"
|
||||
```Python hl_lines="58-65 68-83"
|
||||
{!../../../docs_src/websockets/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -137,6 +137,33 @@ With that you can connect the WebSocket and then send and receive messages:
|
||||
|
||||
<img src="/img/tutorial/websockets/image05.png">
|
||||
|
||||
## Handling disconnections and multiple clients
|
||||
|
||||
When a WebSocket connection is closed, the `await websocket.receive_text()` will raise a `WebSocketDisconnect` exception, which you can then catch and handle like in this example.
|
||||
|
||||
```Python hl_lines="81-83"
|
||||
{!../../../docs_src/websockets/tutorial003.py!}
|
||||
```
|
||||
|
||||
To try it out:
|
||||
|
||||
* Open the app with several browser tabs.
|
||||
* Write messages from them.
|
||||
* Then close one of the tabs.
|
||||
|
||||
That will raise the `WebSocketDisconnect` exception, and all the other clients will receive a message like:
|
||||
|
||||
```
|
||||
Client #1596980209979 left the chat
|
||||
```
|
||||
|
||||
!!! tip
|
||||
The app above is a minimal and simple example to demonstrate how to handle and broadcast messages to several WebSocket connections.
|
||||
|
||||
But have in mind that, as everything is handled in memory, in a single list, it will only work while the process is running, and will only work with a single process.
|
||||
|
||||
If you need something easy to integrate with FastAPI but that is more robust, supported by Redis, PostgreSQL or others, check <a href="https://github.com/encode/broadcaster" class="external-link" target="_blank">encode/broadcaster</a>.
|
||||
|
||||
## More info
|
||||
|
||||
To learn more about the options, check Starlette's documentation for:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Including WSGI - Flask, Django, others
|
||||
|
||||
You can mount WSGI applications as you saw with [Sub Applications - Behind a Proxy, Mounts](./sub-applications-proxy.md){.internal-link target=_blank}.
|
||||
You can mount WSGI applications as you saw with [Sub Applications - Mounts](./sub-applications.md){.internal-link target=_blank}, [Behind a Proxy](./behind-a-proxy.md){.internal-link target=_blank}.
|
||||
|
||||
For that, you can use the `WSGIMiddleware` and use it to wrap your WSGI application, for example, Flask, Django, etc.
|
||||
|
||||
@@ -12,7 +12,7 @@ Then wrap the WSGI (e.g. Flask) app with the middleware.
|
||||
|
||||
And then mount that under a path.
|
||||
|
||||
```Python hl_lines="2 3 22"
|
||||
```Python hl_lines="2-3 22"
|
||||
{!../../../docs_src/wsgi/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -305,7 +305,7 @@ burgers = get_burgers(2)
|
||||
|
||||
So, if you are using a library that tells you that you can call it with `await`, you need to create the *path operation functions* that uses it with `async def`, like in:
|
||||
|
||||
```Python hl_lines="2 3"
|
||||
```Python hl_lines="2-3"
|
||||
@app.get('/burgers')
|
||||
async def read_burgers():
|
||||
burgers = await get_burgers(2)
|
||||
|
||||
@@ -499,13 +499,3 @@ $ bash scripts/test-cov-html.sh
|
||||
</div>
|
||||
|
||||
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:
|
||||
|
||||
```env
|
||||
PYTHONPATH=./docs_src
|
||||
```
|
||||
|
||||
@@ -161,6 +161,8 @@ CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "80"]
|
||||
* Create a `main.py` file with:
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -172,7 +174,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
|
||||
@@ -7,127 +7,72 @@ There are many posts, articles, tools, and projects, related to **FastAPI**.
|
||||
Here's an incomplete list of some of them.
|
||||
|
||||
!!! tip
|
||||
If you have an article, project, tool, or anything related to **FastAPI** that is not yet listed here, create a <a href="https://github.com/tiangolo/fastapi/edit/master/docs/external-links.md" class="external-link" target="_blank">Pull Request adding it</a>.
|
||||
If you have an article, project, tool, or anything related to **FastAPI** that is not yet listed here, create a <a href="https://github.com/tiangolo/fastapi/edit/master/docs/en/data/external_links.yml" class="external-link" target="_blank">Pull Request adding it</a>.
|
||||
|
||||
## Articles
|
||||
|
||||
### English
|
||||
|
||||
* <a href="https://medium.com/@williamhayes/fastapi-starlette-debug-vs-prod-5f7561db3a59" class="external-link" target="_blank">FastAPI/Starlette debug vs prod</a> by <a href="https://medium.com/@williamhayes" class="external-link" target="_blank">William Hayes</a>.
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.english %}
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-google-as-an-external-authentication-provider-3a527672cf33" class="external-link" target="_blank">FastAPI — Google as an external authentication provider</a> by <a href="https://medium.com/@nils_29588" class="external-link" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-how-to-add-basic-and-cookie-authentication-a45c85ef47d3" class="external-link" target="_blank">FastAPI — How to add basic and cookie authentication</a> by <a href="https://medium.com/@nils_29588" class="external-link" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://dev.to/errietta/introduction-to-the-fastapi-python-framework-2n10" class="external-link" target="_blank">Introduction to the fastapi python framework</a> by <a href="https://dev.to/errietta" class="external-link" target="_blank">Errieta Kostala</a>.
|
||||
|
||||
* <a href="http://nickc1.github.io/api,/scikit-learn/2019/01/10/scikit-fastapi.html" class="external-link" target="_blank">FastAPI and Scikit-Learn: Easily Deploy Models</a> by <a href="http://nickc1.github.io/" class="external-link" target="_blank">Nick Cortale</a>.
|
||||
|
||||
* <a href="https://medium.com/data-rebels/fastapi-authentication-revisited-enabling-api-key-authentication-122dc5975680" class="external-link" target="_blank">FastAPI authentication revisited: Enabling API key authentication</a> by <a href="https://medium.com/@nils_29588" class="external-link" target="_blank">Nils de Bruin</a>.
|
||||
|
||||
* <a href="https://medium.com/@nico.axtmann95/deploying-a-scikit-learn-model-with-onnx-und-fastapi-1af398268915" class="external-link" target="_blank">Deploying a scikit-learn model with ONNX and FastAPI</a> by <a href="https://www.linkedin.com/in/nico-axtmann" class="external-link" target="_blank">Nico Axtmann</a>.
|
||||
|
||||
* <a href="https://geekflare.com/python-asynchronous-web-frameworks/" class="external-link" target="_blank">Top 5 Asynchronous Web Frameworks for Python</a> by <a href="https://geekflare.com/author/ankush/" class="external-link" target="_blank">Ankush Thakur</a> on <a href="https://geekflare.com" class="external-link" target="_blank">GeekFlare</a>.
|
||||
|
||||
* <a href="https://medium.com/@gntrm/jwt-authentication-with-fastapi-and-aws-cognito-1333f7f2729e" class="external-link" target="_blank">JWT Authentication with FastAPI and AWS Cognito</a> by <a href="https://twitter.com/gntrm" class="external-link" target="_blank">Johannes Gontrum</a>.
|
||||
|
||||
* <a href="https://towardsdatascience.com/how-to-deploy-a-machine-learning-model-dc51200fe8cf" class="external-link" target="_blank">How to Deploy a Machine Learning Model</a> by <a href="https://www.linkedin.com/in/mgrootendorst/" class="external-link" target="_blank">Maarten Grootendorst</a> on <a href="https://towardsdatascience.com/" class="external-link" target="_blank">Towards Data Science</a>.
|
||||
|
||||
* [Uber: Ludwig v0.2 Adds New Features and Other Improvements to its Deep Learning Toolbox [including a FastAPI server]](https://eng.uber.com/ludwig-v0-2/){.external-link target=_blank} on <a href="https://eng.uber.com" class="external-link" target="_blank">Uber Engineering</a>.
|
||||
|
||||
* <a href="https://gitlab.com/euri10/fastapi_cheatsheet" class="external-link" target="_blank">A FastAPI and Swagger UI visual cheatsheet</a> by <a href="https://gitlab.com/euri10" class="external-link" target="_blank">@euri10</a>
|
||||
|
||||
* <a href="https://medium.com/@mike.p.moritz/using-docker-compose-to-deploy-a-lightweight-python-rest-api-with-a-job-queue-37e6072a209b" class="external-link" target="_blank">Using Docker Compose to deploy a lightweight Python REST API with a job queue</a> by <a href="https://medium.com/@mike.p.moritz" class="external-link" target="_blank">Mike Moritz</a>.
|
||||
|
||||
* <a href="https://robwagner.dev/tortoise-fastapi-setup/" class="external-link" target="_blank">Setting up Tortoise ORM with FastAPI</a> by <a href="https://robwagner.dev/" class="external-link" target="_blank">Rob Wagner</a>.
|
||||
|
||||
* <a href="https://dev.to/dbanty/why-i-m-leaving-flask-3ki6" class="external-link" target="_blank">Why I'm Leaving Flask</a> by <a href="https://dev.to/dbanty" class="external-link" target="_blank">Dylan Anthony</a>.
|
||||
|
||||
* <a href="https://medium.com/python-data/how-to-deploy-tensorflow-2-0-models-as-an-api-service-with-fastapi-docker-128b177e81f3" class="external-link" target="_blank">How To Deploy Tensorflow 2.0 Models As An API Service With FastAPI & Docker</a> by <a href="https://medium.com/@bbrenyah" class="external-link" target="_blank">Bernard Brenyah</a>.
|
||||
|
||||
* <a href="https://testdriven.io/blog/fastapi-crud/" class="external-link" target="_blank">TestDriven.io: Developing and Testing an Asynchronous API with FastAPI and Pytest</a> by <a href="https://testdriven.io/authors/herman/" class="external-link" target="_blank">Michael Herman</a>.
|
||||
|
||||
* <a href="https://towardsdatascience.com/deploying-iris-classifications-with-fastapi-and-docker-7c9b83fdec3a" class="external-link" target="_blank">Towards Data Science: Deploying Iris Classifications with FastAPI and Docker</a> by <a href="https://towardsdatascience.com/@mandygu" class="external-link" target="_blank">Mandy Gu</a>.
|
||||
|
||||
* <a href="https://medium.com/analytics-vidhya/deploy-machine-learning-models-with-keras-fastapi-redis-and-docker-4940df614ece" class="external-link" target="_blank">Deploy Machine Learning Models with Keras, FastAPI, Redis and Docker</a> by <a href="https://medium.com/@shane.soh" class="external-link" target="_blank">Shane Soh</a>.
|
||||
|
||||
* <a href="https://medium.com/@arthur393/another-boilerplate-to-fastapi-azure-pipeline-ci-pytest-3c8d9a4be0bb" class="external-link" target="_blank">Another Boilerplate to FastAPI: Azure Pipeline CI + Pytest</a> by <a href="https://twitter.com/arthurheinrique" class="external-link" target="_blank">Arthur Henrique</a>.
|
||||
|
||||
* <a href="https://iwpnd.pw/articles/2020-01/deploy-fastapi-to-aws-lambda" class="external-link" target="_blank">How to continuously deploy a FastAPI to AWS Lambda with AWS SAM</a> by <a href="https://iwpnd.pw" class="external-link" target="_blank">Benjamin Ramser</a>.
|
||||
|
||||
* <a href="https://www.tutlinks.com/create-and-deploy-fastapi-app-to-heroku/" class="external-link" target="_blank">Create and Deploy FastAPI app to Heroku without using Docker</a> by <a href="https://www.linkedin.com/in/navule/" class="external-link" target="_blank">Navule Pavan Kumar Rao</a>.
|
||||
|
||||
* <a href="https://iwpnd.pw/articles/2020-03/apache-kafka-fastapi-geostream" class="external-link" target="_blank">Apache Kafka producer and consumer with FastAPI and aiokafka</a> by <a href="https://iwpnd.pw" class="external-link" target="_blank">Benjamin Ramser</a>.
|
||||
|
||||
* <a href="https://wuilly.com/2019/10/real-time-notifications-with-python-and-postgres/" class="external-link" target="_blank">Real-time Notifications with Python and Postgres</a> by <a href="https://wuilly.com/" class="external-link" target="_blank">Guillermo Cruz</a>.
|
||||
|
||||
* <a href="https://dev.to/paurakhsharma/microservice-in-python-using-fastapi-24cc" class="external-link" target="_blank">Microservice in Python using FastAPI</a> by <a href="https://twitter.com/PaurakhSharma" class="external-link" target="_blank">Paurakh Sharma Humagain</a>.
|
||||
|
||||
* <a href="https://dev.to/cuongld2/build-simple-api-service-with-python-fastapi-part-1-581o" class="external-link" target="_blank">Build simple API service with Python FastAPI — Part 1</a> by <a href="https://dev.to/cuongld2" class="external-link" target="_blank">cuongld2</a>.
|
||||
|
||||
* <a href="https://paulsec.github.io/posts/fastapi_plus_zeit_serverless_fu/" class="external-link" target="_blank">FastAPI + Zeit.co = 🚀
|
||||
</a> by <a href="https://twitter.com/PaulWebSec" class="external-link" target="_blank">Paul Sec</a>.
|
||||
|
||||
* <a href="https://dev.to/tiangolo/build-a-web-api-from-scratch-with-fastapi-the-workshop-2ehe" class="external-link" target="_blank">Build a web API from scratch with FastAPI - the workshop</a> by <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Sebastián Ramírez (tiangolo)</a>.
|
||||
|
||||
* <a href="https://www.twilio.com/blog/build-secure-twilio-webhook-python-fastapi" class="external-link" target="_blank">Build a Secure Twilio Webhook with Python and FastAPI</a> by <a href="https://www.twilio.com" class="external-link" target="_blank">Twilio</a>.
|
||||
|
||||
* <a href="https://www.stavros.io/posts/fastapi-with-django/" class="external-link" target="_blank">Using FastAPI with Django</a> by <a href="https://twitter.com/Stavros" class="external-link" target="_blank">Stavros Korokithakis</a>.
|
||||
|
||||
* <a href="https://netflixtechblog.com/introducing-dispatch-da4b8a2a8072" class="external-link" target="_blank">Introducing Dispatch</a> by <a href="https://netflixtechblog.com/" class="external-link" target="_blank">Netflix</a>.
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### Japanese
|
||||
|
||||
* <a href="https://qiita.com/mtitg/items/47770e9a562dd150631d" class="external-link" target="_blank">FastAPI|DB接続してCRUDするPython製APIサーバーを構築</a> by <a href="https://qiita.com/mtitg" class="external-link" target="_blank">@mtitg</a>.
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.japanese %}
|
||||
|
||||
* <a href="https://qiita.com/ryoryomaru/items/59958ed385b3571d50de" class="external-link" target="_blank">python製の最新APIフレームワーク FastAPI を触ってみた</a> by <a href="https://qiita.com/ryoryomaru" class="external-link" target="_blank">@ryoryomaru</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/0e1f5dbbe62efc612a78" class="external-link" target="_blank">FastAPIでCORSを回避</a> by <a href="https://qiita.com/angel_katayoku" class="external-link" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/4fbc1a4e2b33fa2237d2" class="external-link" target="_blank">FastAPIをMySQLと接続してDockerで管理してみる</a> by <a href="https://qiita.com/angel_katayoku" class="external-link" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/angel_katayoku/items/8a458a8952f50b73f420" class="external-link" target="_blank">FastAPIでPOSTされたJSONのレスポンスbodyを受け取る</a> by <a href="https://qiita.com/angel_katayoku" class="external-link" target="_blank">@angel_katayoku</a>.
|
||||
|
||||
* <a href="https://qiita.com/hikarut/items/b178af2e2440c67c6ac4" class="external-link" target="_blank">フロントエンド開発者向けのDockerによるPython開発環境構築</a> by <a href="https://qiita.com/hikarut" class="external-link" target="_blank">Hikaru Takahashi</a>.
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-environment" class="external-link" target="_blank">【第1回】FastAPIチュートリアル: ToDoアプリを作ってみよう【環境構築編】</a> by <a href="https://rightcode.co.jp/author/jun" class="external-link" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-model-building" class="external-link" target="_blank">【第2回】FastAPIチュートリアル: ToDoアプリを作ってみよう【モデル構築編】</a> by <a href="https://rightcode.co.jp/author/jun" class="external-link" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-authentication-user-registration" class="external-link" target="_blank">【第3回】FastAPIチュートリアル: toDoアプリを作ってみよう【認証・ユーザ登録編】</a> by <a href="https://rightcode.co.jp/author/jun" class="external-link" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://rightcode.co.jp/blog/information-technology/fastapi-tutorial-todo-apps-admin-page-improvement" class="external-link" target="_blank">【第4回】FastAPIチュートリアル: toDoアプリを作ってみよう【管理者ページ改良編】</a> by <a href="https://rightcode.co.jp/author/jun" class="external-link" target="_blank">ライトコードメディア編集部</a>
|
||||
|
||||
* <a href="https://qiita.com/bee2/items/0ad260ab9835a2087dae" class="external-link" target="_blank">PythonのWeb frameworkのパフォーマンス比較 (Django, Flask, responder, FastAPI, japronto)</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
|
||||
|
||||
* <a href="https://qiita.com/bee2/items/75d9c0d7ba20e7a4a0e9" class="external-link" target="_blank">[FastAPI] Python製のASGI Web フレームワーク FastAPIに入門する</a> by <a href="https://qiita.com/bee2" class="external-link" target="_blank">@bee2</a>.
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### Vietnamese
|
||||
|
||||
* <a href="https://fullstackstation.com/fastapi-trien-khai-bang-docker/" class="external-link" target="_blank">FASTAPI: TRIỂN KHAI BẰNG DOCKER</a> by <a href="https://fullstackstation.com/author/figonking/" class="external-link" target="_blank">Nguyễn Nhân</a>.
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.vietnamese %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### Russian
|
||||
|
||||
* <a href="https://habr.com/ru/post/454440/" class="external-link" target="_blank">Мелкая питонячая радость #2: Starlette - Солидная примочка – FastAPI</a> by <a href="https://habr.com/ru/users/57uff3r/" class="external-link" target="_blank">Andrey Korchak</a>.
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.russian %}
|
||||
|
||||
* <a href="https://habr.com/ru/post/478620/" class="external-link" target="_blank">Почему Вы должны попробовать FastAPI?</a> by <a href="https://github.com/prostomarkeloff" class="external-link" target="_blank">prostomarkeloff</a>.
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
### German
|
||||
|
||||
* <a href="https://blog.codecentric.de/2019/08/inbetriebnahme-eines-scikit-learn-modells-mit-onnx-und-fastapi/" class="external-link" target="_blank">Inbetriebnahme eines scikit-learn-Modells mit ONNX und FastAPI</a> by <a href="https://twitter.com/_nicoax" class="external-link" target="_blank">Nico Axtmann</a>.
|
||||
{% if external_links %}
|
||||
{% for article in external_links.articles.german %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Podcasts
|
||||
|
||||
* <a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" class="external-link" target="_blank">FastAPI on PythonBytes</a> by <a href="https://pythonbytes.fm/" class="external-link" target="_blank">Python Bytes FM</a>.
|
||||
* <a href="https://www.pythonpodcast.com/fastapi-web-application-framework-episode-259/" class="external-link" target="_blank">Build The Next Generation Of Python Web Applications With FastAPI - Episode 259 - interview to Sebastían Ramírez (tiangolo)</a> by <a href="https://www.pythonpodcast.com/" class="external-link" target="_blank">Podcast.`__init__`</a>.
|
||||
{% if external_links %}
|
||||
{% for article in external_links.podcasts.english %}
|
||||
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Talks
|
||||
|
||||
* <a href="https://www.youtube.com/watch?v=3DLwPcrE5mA" class="external-link" target="_blank">PyCon UK 2019: FastAPI from the ground up</a> by <a href="https://twitter.com/chriswithers13" class="external-link" target="_blank">Chris Withers</a>.
|
||||
{% if external_links %}
|
||||
{% for article in external_links.talks.english %}
|
||||
|
||||
* <a href="https://www.youtube.com/watch?v=z9K5pwb0rt8" class="external-link" target="_blank">PyConBY 2020: Serve ML models easily with FastAPI</a> by <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Sebastián Ramírez (tiangolo)</a>.
|
||||
|
||||
* <a href="https://www.youtube.com/watch?v=PnpTY1f4k2U" class="external-link" target="_blank">[VIRTUAL] Py.Amsterdam's flying Software Circus: Intro to FastAPI</a> by <a href="https://twitter.com/tiangolo" class="external-link" target="_blank">Sebastián Ramírez (tiangolo)</a>.
|
||||
* <a href="{{ article.link }}" class="external-link" target="_blank">{{ article.title }}</a> by <a href="{{ article.author_link }}" class="external-link" target="_blank">{{ article.author }}</a>.
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
## Projects
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 80 KiB After Width: | Height: | Size: 71 KiB |
|
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 61 KiB |
BIN
docs/en/docs/img/tutorial/behind-a-proxy/image03.png
Normal file
|
After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 82 KiB After Width: | Height: | Size: 82 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 80 KiB |
|
Before Width: | Height: | Size: 86 KiB After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 79 KiB After Width: | Height: | Size: 76 KiB |
@@ -5,14 +5,14 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.com/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.com/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
<a href="https://github.com/tiangolo/fastapi/actions?query=workflow%3ATest" target="_blank">
|
||||
<img src="https://github.com/tiangolo/fastapi/workflows/Test/badge.svg" alt="Test">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi" alt="Coverage">
|
||||
<img src="https://img.shields.io/codecov/c/github/tiangolo/fastapi?color=%2334D058" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
|
||||
<img src="https://img.shields.io/pypi/v/fastapi?color=%2334D058&label=pypi%20package" alt="Package version">
|
||||
</a>
|
||||
<a href="https://gitter.im/tiangolo/fastapi?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge" target="_blank">
|
||||
<img src="https://badges.gitter.im/tiangolo/fastapi.svg" alt="Join the chat at https://gitter.im/tiangolo/fastapi">
|
||||
@@ -131,6 +131,8 @@ $ pip install uvicorn
|
||||
* Create a file `main.py` with:
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -142,7 +144,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -151,7 +153,9 @@ def read_item(item_id: int, q: str = None):
|
||||
|
||||
If your code uses `async` / `await`, use `async def`:
|
||||
|
||||
```Python hl_lines="7 12"
|
||||
```Python hl_lines="9 14"
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
@@ -163,7 +167,7 @@ async def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str = None):
|
||||
async def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
@@ -241,7 +245,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.
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 23 24 25"
|
||||
```Python hl_lines="4 9-12 25-27"
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -251,7 +257,7 @@ app = FastAPI()
|
||||
class Item(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
is_offer: bool = None
|
||||
is_offer: Optional[bool] = None
|
||||
|
||||
|
||||
@app.get("/")
|
||||
@@ -260,7 +266,7 @@ def read_root():
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
def read_item(item_id: int, q: Optional[str] = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
|
||||
@@ -193,7 +193,7 @@ And still, the editor knows it is a `str`, and provides support for that.
|
||||
|
||||
You would do the same to declare `tuple`s and `set`s:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -210,7 +210,7 @@ The first type parameter is for the keys of the `dict`.
|
||||
|
||||
The second type parameter is for the values of the `dict`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
@@ -224,7 +224,7 @@ This means:
|
||||
|
||||
You can also use `Optional` to declare that a variable has a type, like `str`, but that it is "optional", which means that it could also be `None`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
```Python hl_lines="1 4"
|
||||
{!../../../docs_src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
@@ -249,7 +249,7 @@ You can also declare a class as the type of a variable.
|
||||
|
||||
Let's say you have a class `Person`, with a name:
|
||||
|
||||
```Python hl_lines="1 2 3"
|
||||
```Python hl_lines="1-3"
|
||||
{!../../../docs_src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,6 +2,149 @@
|
||||
|
||||
## Latest changes
|
||||
|
||||
## 0.61.1
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fix issues using `jsonable_encoder` with SQLAlchemy models directly. PR [#1987](https://github.com/tiangolo/fastapi/pull/1987).
|
||||
|
||||
### Docs
|
||||
|
||||
* Fix typo in NoSQL docs. PR [#1980](https://github.com/tiangolo/fastapi/pull/1980) by [@facundojmaero](https://github.com/facundojmaero).
|
||||
|
||||
### Translations
|
||||
|
||||
* Add translation for [main page to Japanese](https://fastapi.tiangolo.com/ja/) PR [#1571](https://github.com/tiangolo/fastapi/pull/1571) by [@ryuckel](https://github.com/ryuckel).
|
||||
* Initialize French translations. PR [#1975](https://github.com/tiangolo/fastapi/pull/1975) by [@JulianMaurin-BM](https://github.com/JulianMaurin-BM).
|
||||
* Initialize Turkish translations. PR [#1905](https://github.com/tiangolo/fastapi/pull/1905) by [@ycd](https://github.com/ycd).
|
||||
|
||||
### Internal
|
||||
|
||||
* Improve docs maintainability by updating `hl_lines` syntax to use ranges. PR [#1863](https://github.com/tiangolo/fastapi/pull/1863) by [@la-mar](https://github.com/la-mar).
|
||||
|
||||
## 0.61.0
|
||||
|
||||
### Features
|
||||
|
||||
* Add support for injecting `HTTPConnection` (as `Request` and `WebSocket`). Useful for sharing app state in dependencies. PR [#1827](https://github.com/tiangolo/fastapi/pull/1827) by [@nsidnev](https://github.com/nsidnev).
|
||||
* Export `WebSocketDisconnect` and add example handling WebSocket disconnections to docs. PR [#1822](https://github.com/tiangolo/fastapi/pull/1822) by [@rkbeatss](https://github.com/rkbeatss).
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
* Require Pydantic > `1.0.0`.
|
||||
* Remove support for deprecated Pydantic `0.32.2`. This improves maintainability and allows new features.
|
||||
* In `FastAPI` and `APIRouter`:
|
||||
* Remove *path operation decorators* related/deprecated parameter `response_model_skip_defaults` (use `response_model_exclude_unset` instead).
|
||||
* Change *path operation decorators* parameter default for `response_model_exclude` from `set()` to `None` (as is in Pydantic).
|
||||
* In `encoders.jsonable_encoder`:
|
||||
* Remove deprecated `skip_defaults`, use instead `exclude_unset`.
|
||||
* Set default of `exclude` from `set()` to `None` (as is in Pydantic).
|
||||
* PR [#1862](https://github.com/tiangolo/fastapi/pull/1862).
|
||||
* In `encoders.jsonable_encoder` remove parameter `sqlalchemy_safe`.
|
||||
* It was an early hack to allow returning SQLAlchemy models, but it was never documented, and the recommended way is using Pydantic's `orm_mode` as described in the tutorial: [SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/sql-databases/).
|
||||
* PR [#1864](https://github.com/tiangolo/fastapi/pull/1864).
|
||||
|
||||
### Docs
|
||||
|
||||
* Add link to the course by TestDriven.io: [Test-Driven Development with FastAPI and Docker](https://testdriven.io/courses/tdd-fastapi/). PR [#1860](https://github.com/tiangolo/fastapi/pull/1860).
|
||||
* Fix empty log message in docs example about handling errors. PR [#1815](https://github.com/tiangolo/fastapi/pull/1815) by [@manlix](https://github.com/manlix).
|
||||
* Reword text to reduce ambiguity while not being gender-specific. PR [#1824](https://github.com/tiangolo/fastapi/pull/1824) by [@Mause](https://github.com/Mause).
|
||||
|
||||
### Internal
|
||||
|
||||
* Add Flake8 linting. Original PR [#1774](https://github.com/tiangolo/fastapi/pull/1774) by [@MashhadiNima](https://github.com/MashhadiNima).
|
||||
* Disable Gitter bot, as it's currently broken, and Gitter's response doesn't show the problem. PR [#1853](https://github.com/tiangolo/fastapi/pull/1853).
|
||||
|
||||
## 0.60.2
|
||||
|
||||
* Fix typo in docs for query parameters. PR [#1832](https://github.com/tiangolo/fastapi/pull/1832) by [@ycd](https://github.com/ycd).
|
||||
* Add docs about [Async Tests](https://fastapi.tiangolo.com/advanced/async-tests/). PR [#1619](https://github.com/tiangolo/fastapi/pull/1619) by [@empicano](https://github.com/empicano).
|
||||
* Raise an exception when using form data (`Form`, `File`) without having `python-multipart` installed.
|
||||
* Up to now the application would run, and raise an exception only when receiving a request with form data, the new behavior, raising early, will prevent from deploying applications with broken dependencies.
|
||||
* It also detects if the correct package `python-multipart` is installed instead of the incorrect `multipart` (both importable as `multipart`).
|
||||
* PR [#1851](https://github.com/tiangolo/fastapi/pull/1851) based on original PR [#1627](https://github.com/tiangolo/fastapi/pull/1627) by [@chrisngyn](https://github.com/chrisngyn), [@YKo20010](https://github.com/YKo20010), [@kx-chen](https://github.com/kx-chen).
|
||||
* Re-enable Gitter releases bot. PR [#1831](https://github.com/tiangolo/fastapi/pull/1831).
|
||||
* Add link to async SQL databases tutorial from main SQL tutorial. PR [#1813](https://github.com/tiangolo/fastapi/pull/1813) by [@short2strings](https://github.com/short2strings).
|
||||
* Fix typo in tutorial about behind a proxy. PR [#1807](https://github.com/tiangolo/fastapi/pull/1807) by [@toidi](https://github.com/toidi).
|
||||
* Fix typo in Portuguese docs. PR [#1795](https://github.com/tiangolo/fastapi/pull/1795) by [@izaguerreiro](https://github.com/izaguerreiro).
|
||||
* Add translations setup for Ukrainian. PR [#1830](https://github.com/tiangolo/fastapi/pull/1830).
|
||||
* Add external link [Build And Host Fast Data Science Applications Using FastAPI](https://towardsdatascience.com/build-and-host-fast-data-science-applications-using-fastapi-823be8a1d6a0). PR [#1786](https://github.com/tiangolo/fastapi/pull/1786) by [@Kludex](https://github.com/Kludex).
|
||||
* Fix encoding of Pydantic models that inherit from others models with custom `json_encoders`. PR [#1769](https://github.com/tiangolo/fastapi/pull/1769) by [@henrybetts](https://github.com/henrybetts).
|
||||
* Simplify and improve `jsonable_encoder`. PR [#1754](https://github.com/tiangolo/fastapi/pull/1754) by [@MashhadiNima](https://github.com/MashhadiNima).
|
||||
* Simplify internal code syntax in several points. PR [#1753](https://github.com/tiangolo/fastapi/pull/1753) by [@uriyyo](https://github.com/uriyyo).
|
||||
* Improve internal typing, declare `Optional` parameters. PR [#1731](https://github.com/tiangolo/fastapi/pull/1731) by [@MashhadiNima](https://github.com/MashhadiNima).
|
||||
* Add external link [Deploy FastAPI on Azure App Service](https://www.tutlinks.com/deploy-fastapi-on-azure/) to docs. PR [#1726](https://github.com/tiangolo/fastapi/pull/1726) by [@windson](https://github.com/windson).
|
||||
* Add link to Starlette docs about WebSocket testing. PR [#1717](https://github.com/tiangolo/fastapi/pull/1717) by [@hellocoldworld](https://github.com/hellocoldworld).
|
||||
* Refactor generating dependant, merge for loops. PR [#1714](https://github.com/tiangolo/fastapi/pull/1714) by [@Bloodielie](https://github.com/Bloodielie).
|
||||
* Update example for templates with Jinja to include HTML media type. PR [#1690](https://github.com/tiangolo/fastapi/pull/1690) by [@frafra](https://github.com/frafra).
|
||||
* Fix typos in docs for security. PR [#1678](https://github.com/tiangolo/fastapi/pull/1678) by [@nilslindemann](https://github.com/nilslindemann).
|
||||
* Fix typos in docs for dependencies. PR [#1675](https://github.com/tiangolo/fastapi/pull/1675) by [@nilslindemann](https://github.com/nilslindemann).
|
||||
* Fix type annotation for `**extra` parameters in `FastAPI`. PR [#1659](https://github.com/tiangolo/fastapi/pull/1659) by [@bharel](https://github.com/bharel).
|
||||
* Bump MkDocs Material to fix docs in browsers with dark mode. PR [#1789](https://github.com/tiangolo/fastapi/pull/1789) by [@adriencaccia](https://github.com/adriencaccia).
|
||||
* Remove docs preview comment from each commit. PR [#1826](https://github.com/tiangolo/fastapi/pull/1826).
|
||||
* Update GitHub context extraction for Gitter notification bot. PR [#1766](https://github.com/tiangolo/fastapi/pull/1766).
|
||||
|
||||
## 0.60.1
|
||||
|
||||
* Add debugging logs for GitHub actions to introspect GitHub hidden context. PR [#1764](https://github.com/tiangolo/fastapi/pull/1764).
|
||||
* Use OS preference theme for online docs. PR [#1760](https://github.com/tiangolo/fastapi/pull/1760) by [@adriencaccia](https://github.com/adriencaccia).
|
||||
* Upgrade Starlette to version `0.13.6` to handle a vulnerability when using static files in Windows. PR [#1759](https://github.com/tiangolo/fastapi/pull/1759) by [@jamesag26](https://github.com/jamesag26).
|
||||
* Pin Swagger UI temporarily, waiting for a fix for [swagger-api/swagger-ui#6249](https://github.com/swagger-api/swagger-ui/issues/6249). PR [#1763](https://github.com/tiangolo/fastapi/pull/1763).
|
||||
* Update GitHub Actions, use commit from PR for docs preview, not commit from pre-merge. PR [#1761](https://github.com/tiangolo/fastapi/pull/1761).
|
||||
* Update GitHub Actions, refactor Gitter bot. PR [#1746](https://github.com/tiangolo/fastapi/pull/1746).
|
||||
|
||||
## 0.60.0
|
||||
|
||||
* Add GitHub Action to watch for missing preview docs and trigger a preview deploy. PR [#1740](https://github.com/tiangolo/fastapi/pull/1740).
|
||||
* Add custom GitHub Action to get artifact with docs preview. PR [#1739](https://github.com/tiangolo/fastapi/pull/1739).
|
||||
* Add new GitHub Actions to preview docs from PRs. PR [#1738](https://github.com/tiangolo/fastapi/pull/1738).
|
||||
* Add XML test coverage to support GitHub Actions. PR [#1737](https://github.com/tiangolo/fastapi/pull/1737).
|
||||
* Update badges and remove Travis now that GitHub Actions is the main CI. PR [#1736](https://github.com/tiangolo/fastapi/pull/1736).
|
||||
* Add GitHub Actions for CI, move from Travis. PR [#1735](https://github.com/tiangolo/fastapi/pull/1735).
|
||||
* Add support for adding OpenAPI schema for GET requests with a body. PR [#1626](https://github.com/tiangolo/fastapi/pull/1626) by [@victorphoenix3](https://github.com/victorphoenix3).
|
||||
|
||||
## 0.59.0
|
||||
|
||||
* Fix typo in docstring for OAuth2 utils. PR [#1621](https://github.com/tiangolo/fastapi/pull/1621) by [@tomarv2](https://github.com/tomarv2).
|
||||
* Update JWT docs to use Python-jose instead of PyJWT. Initial PR [#1610](https://github.com/tiangolo/fastapi/pull/1610) by [@asheux](https://github.com/asheux).
|
||||
* Fix/re-enable search bar in docs. PR [#1703](https://github.com/tiangolo/fastapi/pull/1703).
|
||||
* Auto-generate a "server" in OpenAPI `servers` when there's a `root_path` instead of prefixing all the `paths`:
|
||||
* Add a new parameter for `FastAPI` classes: `root_path_in_servers` to disable the auto-generation of `servers`.
|
||||
* New docs about `root_path` and `servers` in [Additional Servers](https://fastapi.tiangolo.com/advanced/behind-a-proxy/#additional-servers).
|
||||
* Update OAuth2 examples to use a relative URL for `tokenUrl="token"` to make sure those examples keep working as-is even when behind a reverse proxy.
|
||||
* Initial PR [#1596](https://github.com/tiangolo/fastapi/pull/1596) by [@rkbeatss](https://github.com/rkbeatss).
|
||||
* Fix typo/link in External Links. PR [#1702](https://github.com/tiangolo/fastapi/pull/1702).
|
||||
* Update handling of [External Links](https://fastapi.tiangolo.com/external-links/) to use a data file and allow translating the headers without becoming obsolete quickly when new links are added. PR [#https://github.com/tiangolo/fastapi/pull/1701](https://github.com/tiangolo/fastapi/pull/1701).
|
||||
* Add external link [Machine learning model serving in Python using FastAPI and Streamlit](https://davidefiocco.github.io/2020/06/27/streamlit-fastapi-ml-serving.html) to docs. PR [#1669](https://github.com/tiangolo/fastapi/pull/1669) by [@davidefiocco](https://github.com/davidefiocco).
|
||||
* Add note in docs on order in Pydantic Unions. PR [#1591](https://github.com/tiangolo/fastapi/pull/1591) by [@kbanc](https://github.com/kbanc).
|
||||
* Improve support for tests in editor. PR [#1699](https://github.com/tiangolo/fastapi/pull/1699).
|
||||
* Pin dependencies. PR [#1697](https://github.com/tiangolo/fastapi/pull/1697).
|
||||
* Update isort to version 5.x.x. PR [#1670](https://github.com/tiangolo/fastapi/pull/1670) by [@asheux](https://github.com/asheux).
|
||||
|
||||
## 0.58.1
|
||||
|
||||
* Add link in docs to Pydantic data types. PR [#1612](https://github.com/tiangolo/fastapi/pull/1612) by [@tayoogunbiyi](https://github.com/tayoogunbiyi).
|
||||
* Fix link in warning logs for `openapi_prefix`. PR [#1611](https://github.com/tiangolo/fastapi/pull/1611) by [@bavaria95](https://github.com/bavaria95).
|
||||
* Fix bad link in docs. PR [#1603](https://github.com/tiangolo/fastapi/pull/1603) by [@molto0504](https://github.com/molto0504).
|
||||
* Add Vim temporary files to `.gitignore` for contributors using Vim. PR [#1590](https://github.com/tiangolo/fastapi/pull/1590) by [@asheux](https://github.com/asheux).
|
||||
* Fix typo in docs for sub-applications. PR [#1578](https://github.com/tiangolo/fastapi/pull/1578) by [@schlpbch](https://github.com/schlpbch).
|
||||
* Use `Optional` in all the examples in the docs. Original PR [#1574](https://github.com/tiangolo/fastapi/pull/1574) by [@chrisngyn](https://github.com/chrisngyn), [@kx-chen](https://github.com/kx-chen), [@YKo20010](https://github.com/YKo20010). Updated and merged PR [#1644](https://github.com/tiangolo/fastapi/pull/1644).
|
||||
* Update tests and handling of `response_model_by_alias`. PR [#1642](https://github.com/tiangolo/fastapi/pull/1642).
|
||||
* Add translation to Chinese for [Body - Fields - 请求体 - 字段](https://fastapi.tiangolo.com/zh/tutorial/body-fields/). PR [#1569](https://github.com/tiangolo/fastapi/pull/1569) by [@waynerv](https://github.com/waynerv).
|
||||
* Update Chinese translation of main page. PR [#1564](https://github.com/tiangolo/fastapi/pull/1564) by [@waynerv](https://github.com/waynerv).
|
||||
* Add translation to Chinese for [Body - Multiple Parameters - 请求体 - 多个参数](https://fastapi.tiangolo.com/zh/tutorial/body-multiple-params/). PR [#1532](https://github.com/tiangolo/fastapi/pull/1532) by [@waynerv](https://github.com/waynerv).
|
||||
* Add translation to Chinese for [Path Parameters and Numeric Validations - 路径参数和数值校验](https://fastapi.tiangolo.com/zh/tutorial/path-params-numeric-validations/). PR [#1506](https://github.com/tiangolo/fastapi/pull/1506) by [@waynerv](https://github.com/waynerv).
|
||||
* Add GitHub action to auto-label approved PRs (mainly for translations). PR [#1638](https://github.com/tiangolo/fastapi/pull/1638).
|
||||
|
||||
## 0.58.0
|
||||
|
||||
* Deep merge OpenAPI responses to preserve all the additional metadata. PR [#1577](https://github.com/tiangolo/fastapi/pull/1577).
|
||||
* Mention in docs that only main app events are run (not sub-apps). PR [#1554](https://github.com/tiangolo/fastapi/pull/1554) by [@amacfie](https://github.com/amacfie).
|
||||
* Fix body validation error response, do not include body variable when it is not embedded. PR [#1553](https://github.com/tiangolo/fastapi/pull/1553) by [@amacfie](https://github.com/amacfie).
|
||||
* Fix testing OAuth2 security scopes when using dependency overrides. PR [#1549](https://github.com/tiangolo/fastapi/pull/1549) by [@amacfie](https://github.com/amacfie).
|
||||
* Fix Model for JSON Schema keyword `not` as a JSON Schema instead of a list. PR [#1548](https://github.com/tiangolo/fastapi/pull/1548) by [@v-do](https://github.com/v-do).
|
||||
* Add support for OpenAPI `servers`. PR [#1547](https://github.com/tiangolo/fastapi/pull/1547) by [@mikaello](https://github.com/mikaello).
|
||||
|
||||
## 0.57.0
|
||||
|
||||
* Remove broken link from "External Links". PR [#1565](https://github.com/tiangolo/fastapi/pull/1565) by [@victorphoenix3](https://github.com/victorphoenix3).
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
You can define background tasks to be run *after* returning a response.
|
||||
|
||||
This is useful for operations that need to happen after a request, but that the client doesn't really have to be waiting for the operation to complete before receiving his response.
|
||||
This is useful for operations that need to happen after a request, but that the client doesn't really have to be waiting for the operation to complete before receiving the response.
|
||||
|
||||
This includes, for example:
|
||||
|
||||
@@ -15,7 +15,7 @@ This includes, for example:
|
||||
|
||||
First, import `BackgroundTasks` and define a parameter in your *path operation function* with a type declaration of `BackgroundTasks`:
|
||||
|
||||
```Python hl_lines="1 13"
|
||||
```Python hl_lines="1 13"
|
||||
{!../../../docs_src/background_tasks/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ In this case, the task function will write to a file (simulating sending an emai
|
||||
|
||||
And as the write operation doesn't use `async` and `await`, we define the function with normal `def`:
|
||||
|
||||
```Python hl_lines="6 7 8 9"
|
||||
```Python hl_lines="6-9"
|
||||
{!../../../docs_src/background_tasks/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -57,7 +57,7 @@ Using `BackgroundTasks` also works with the dependency injection system, you can
|
||||
|
||||
**FastAPI** knows what to do in each case and how to re-use the same object, so that all the background tasks are merged together and are run in the background afterwards:
|
||||
|
||||
```Python hl_lines="11 14 20 23"
|
||||
```Python hl_lines="13 15 22 25"
|
||||
{!../../../docs_src/background_tasks/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ You can create the *path operations* for that module using `APIRouter`.
|
||||
|
||||
You import it and create an "instance" the same way you would with the class `FastAPI`:
|
||||
|
||||
```Python hl_lines="1 3"
|
||||
```Python hl_lines="1 3"
|
||||
{!../../../docs_src/bigger_applications/app/routers/users.py!}
|
||||
```
|
||||
|
||||
@@ -70,7 +70,7 @@ And then you use it to declare your *path operations*.
|
||||
|
||||
Use it the same way you would use the `FastAPI` class:
|
||||
|
||||
```Python hl_lines="6 11 16"
|
||||
```Python hl_lines="6 11 16"
|
||||
{!../../../docs_src/bigger_applications/app/routers/users.py!}
|
||||
```
|
||||
|
||||
@@ -100,7 +100,7 @@ But let's say that this time we are more lazy.
|
||||
|
||||
And we don't want to have to explicitly type `/items/` and `tags=["items"]` in every *path operation* (we will be able to do it later):
|
||||
|
||||
```Python hl_lines="6 11"
|
||||
```Python hl_lines="6 11"
|
||||
{!../../../docs_src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
|
||||
@@ -110,7 +110,7 @@ We are not adding the prefix `/items/` nor the `tags=["items"]` to add them late
|
||||
|
||||
But we can add custom `tags` and `responses` that will be applied to a specific *path operation*:
|
||||
|
||||
```Python hl_lines="18 19"
|
||||
```Python hl_lines="18-19"
|
||||
{!../../../docs_src/bigger_applications/app/routers/items.py!}
|
||||
```
|
||||
|
||||
@@ -126,7 +126,7 @@ This will be the main file in your application that ties everything together.
|
||||
|
||||
You import and create a `FastAPI` class as normally:
|
||||
|
||||
```Python hl_lines="1 5"
|
||||
```Python hl_lines="1 5"
|
||||
{!../../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
@@ -245,7 +245,7 @@ And we can add predefined `responses` that will be included in all the *path ope
|
||||
|
||||
And we can add a list of `dependencies` that will be added to all the *path operations* in the router and will be executed/solved for each request made to them. Note that, much like dependencies in *path operation decorators*, no value will be passed to your *path operation function*.
|
||||
|
||||
```Python hl_lines="8 9 10 14 15 16 17 18 19 20"
|
||||
```Python hl_lines="8-10 14-20"
|
||||
{!../../../docs_src/bigger_applications/app/main.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ The same way you can declare additional validation and metadata in *path operati
|
||||
|
||||
First, you have to import it:
|
||||
|
||||
```Python hl_lines="2"
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@ First, you have to import it:
|
||||
|
||||
You can then use `Field` with model attributes:
|
||||
|
||||
```Python hl_lines="9 10"
|
||||
```Python hl_lines="11-14"
|
||||
{!../../../docs_src/body_fields/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ First, of course, you can mix `Path`, `Query` and request body parameter declara
|
||||
|
||||
And you can also declare body parameters as optional, by setting the default to `None`:
|
||||
|
||||
```Python hl_lines="17 18 19"
|
||||
```Python hl_lines="19-21"
|
||||
{!../../../docs_src/body_multiple_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ In the previous example, the *path operations* would expect a JSON body with the
|
||||
|
||||
But you can also declare multiple body parameters, e.g. `item` and `user`:
|
||||
|
||||
```Python hl_lines="20"
|
||||
```Python hl_lines="22"
|
||||
{!../../../docs_src/body_multiple_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -72,7 +72,7 @@ If you declare it as is, because it is a singular value, **FastAPI** will assume
|
||||
But you can instruct **FastAPI** to treat it as another body key using `Body`:
|
||||
|
||||
|
||||
```Python hl_lines="21"
|
||||
```Python hl_lines="23"
|
||||
{!../../../docs_src/body_multiple_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -104,12 +104,12 @@ Of course, you can also declare additional query parameters whenever you need, a
|
||||
As, by default, singular values are interpreted as query parameters, you don't have to explicitly add a `Query`, you can just do:
|
||||
|
||||
```Python
|
||||
q: str = None
|
||||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="25"
|
||||
```Python hl_lines="27"
|
||||
{!../../../docs_src/body_multiple_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -131,7 +131,7 @@ item: Item = Body(..., embed=True)
|
||||
|
||||
as in:
|
||||
|
||||
```Python hl_lines="15"
|
||||
```Python hl_lines="17"
|
||||
{!../../../docs_src/body_multiple_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,15 +6,15 @@ With **FastAPI**, you can define, validate, document, and use arbitrarily deeply
|
||||
|
||||
You can define an attribute to be a subtype. For example, a Python `list`:
|
||||
|
||||
```Python hl_lines="12"
|
||||
```Python hl_lines="14"
|
||||
{!../../../docs_src/body_nested_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
This will make `tags` be a list of items. Although it doesn't declare the type of each of the items.
|
||||
|
||||
## List fields with subtype
|
||||
## List fields with type parameter
|
||||
|
||||
But Python has a specific way to declare lists with subtypes:
|
||||
But Python has a specific way to declare lists with internal types, or "type parameters":
|
||||
|
||||
### Import typing's `List`
|
||||
|
||||
@@ -24,12 +24,12 @@ First, import `List` from standard Python's `typing` module:
|
||||
{!../../../docs_src/body_nested_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
### Declare a `List` with a subtype
|
||||
### Declare a `List` with a type parameter
|
||||
|
||||
To declare types that have subtypes, like `list`, `dict`, `tuple`:
|
||||
To declare types that have type parameters (internal types), like `list`, `dict`, `tuple`:
|
||||
|
||||
* Import them from the `typing` module
|
||||
* Pass the subtype(s) as "type arguments" using square brackets: `[` and `]`
|
||||
* Pass the internal type(s) as "type parameters" using square brackets: `[` and `]`
|
||||
|
||||
```Python
|
||||
from typing import List
|
||||
@@ -39,7 +39,7 @@ my_list: List[str]
|
||||
|
||||
That's all standard Python syntax for type declarations.
|
||||
|
||||
Use that same standard syntax for model attributes with subtypes.
|
||||
Use that same standard syntax for model attributes with internal types.
|
||||
|
||||
So, in our example, we can make `tags` be specifically a "list of strings":
|
||||
|
||||
@@ -55,7 +55,7 @@ And Python has a special data type for sets of unique items, the `set`.
|
||||
|
||||
Then we can import `Set` and declare `tags` as a `set` of `str`:
|
||||
|
||||
```Python hl_lines="1 14"
|
||||
```Python hl_lines="1 14"
|
||||
{!../../../docs_src/body_nested_models/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -71,7 +71,7 @@ Each attribute of a Pydantic model has a type.
|
||||
|
||||
But that type can itself be another Pydantic model.
|
||||
|
||||
So, you can declare deeply nested JSON `object`s with specific attribute names, types and validations.
|
||||
So, you can declare deeply nested JSON "objects" with specific attribute names, types and validations.
|
||||
|
||||
All that, arbitrarily nested.
|
||||
|
||||
@@ -79,7 +79,7 @@ All that, arbitrarily nested.
|
||||
|
||||
For example, we can define an `Image` model:
|
||||
|
||||
```Python hl_lines="9 10 11"
|
||||
```Python hl_lines="9-11"
|
||||
{!../../../docs_src/body_nested_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -122,7 +122,7 @@ To see all the options you have, checkout the docs for <a href="https://pydantic
|
||||
|
||||
For example, as in the `Image` model we have a `url` field, we can declare it to be instead of a `str`, a Pydantic's `HttpUrl`:
|
||||
|
||||
```Python hl_lines="4 10"
|
||||
```Python hl_lines="4 10"
|
||||
{!../../../docs_src/body_nested_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -169,12 +169,12 @@ This will expect (convert, validate, document, etc) a JSON body like:
|
||||
|
||||
You can define arbitrarily deeply nested models:
|
||||
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
```Python hl_lines="9 14 20 23 27"
|
||||
{!../../../docs_src/body_nested_models/tutorial007.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Notice how `Offer` as a list of `Item`s, which in turn have an optional list of `Image`s
|
||||
Notice how `Offer` has a list of `Item`s, which in turn have an optional list of `Image`s
|
||||
|
||||
## Bodies of pure lists
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ To update an item you can use the <a href="https://developer.mozilla.org/en-US/d
|
||||
|
||||
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
|
||||
|
||||
```Python hl_lines="30 31 32 33 34 35"
|
||||
```Python hl_lines="30-35"
|
||||
{!../../../docs_src/body_updates/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -82,7 +82,7 @@ In summary, to apply partial updates you would:
|
||||
* Save the data to your DB.
|
||||
* Return the updated model.
|
||||
|
||||
```Python hl_lines="30 31 32 33 34 35 36 37"
|
||||
```Python hl_lines="30-37"
|
||||
{!../../../docs_src/body_updates/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -9,15 +9,17 @@ Your API almost always has to send a **response** body. But clients don't necess
|
||||
To declare a **request** body, you use <a href="https://pydantic-docs.helpmanual.io/" class="external-link" target="_blank">Pydantic</a> models with all their power and benefits.
|
||||
|
||||
!!! info
|
||||
You cannot send a request body using a `GET` operation (HTTP method).
|
||||
To send data, you should use one of: `POST` (the more common), `PUT`, `DELETE` or `PATCH`.
|
||||
|
||||
To send data, you have to use one of: `POST` (the more common), `PUT`, `DELETE` or `PATCH`.
|
||||
Sending a body with a `GET` request has an undefined behavior in the specifications, nevertheless, it is supported by FastAPI, only for very complex/extreme use cases.
|
||||
|
||||
As it is discouraged, the interactive docs with Swagger UI won't show the documentation for the body when using `GET`, and proxies in the middle might not support it.
|
||||
|
||||
## Import Pydantic's `BaseModel`
|
||||
|
||||
First, you need to import `BaseModel` from `pydantic`:
|
||||
|
||||
```Python hl_lines="2"
|
||||
```Python hl_lines="4"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -27,7 +29,7 @@ Then you declare your data model as a class that inherits from `BaseModel`.
|
||||
|
||||
Use standard Python types for all the attributes:
|
||||
|
||||
```Python hl_lines="5 6 7 8 9"
|
||||
```Python hl_lines="7-11"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -57,7 +59,7 @@ For example, this model above declares a JSON "`object`" (or Python `dict`) like
|
||||
|
||||
To add it to your *path operation*, declare it the same way you declared path and query parameters:
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -123,7 +125,7 @@ But you would get the same editor support with <a href="https://www.jetbrains.co
|
||||
|
||||
Inside of the function, you can access all the attributes of the model object directly:
|
||||
|
||||
```Python hl_lines="19"
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/body/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -133,7 +135,7 @@ You can declare path parameters and body requests at the same time.
|
||||
|
||||
**FastAPI** will recognize that the function parameters that match path parameters should be **taken from the path**, and that function parameters that are declared to be Pydantic models should be **taken from the request body**.
|
||||
|
||||
```Python hl_lines="15 16"
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/body/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -143,7 +145,7 @@ You can also declare **body**, **path** and **query** parameters, all at the sam
|
||||
|
||||
**FastAPI** will recognize each of them and take the data from the correct place.
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/body/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -153,6 +155,11 @@ The function parameters will be recognized as follows:
|
||||
* If the parameter is of a **singular type** (like `int`, `float`, `str`, `bool`, etc) it will be interpreted as a **query** parameter.
|
||||
* If the parameter is declared to be of the type of a **Pydantic model**, it will be interpreted as a request **body**.
|
||||
|
||||
!!! note
|
||||
FastAPI will know that the value of `q` is not required because of the default value `= None`.
|
||||
|
||||
The `Optional` in `Optional[str]` is not used by FastAPI, but will allow your editor to give you better support and detect errors.
|
||||
|
||||
## Without Pydantic
|
||||
|
||||
If you don't want to use Pydantic models, you can also use **Body** parameters. See the docs for [Body - Multiple Parameters: Singular values in body](body-multiple-params.md#singular-values-in-body){.internal-link target=_blank}.
|
||||
|
||||
@@ -6,7 +6,7 @@ You can define Cookie parameters the same way you define `Query` and `Path` para
|
||||
|
||||
First import `Cookie`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ Then declare the cookie parameters using the same structure as with `Path` and `
|
||||
|
||||
The first value is the default value, you can pass all the extra validation or annotation parameters:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/cookie_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ You can also specify if your backend allows:
|
||||
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
|
||||
* Specific HTTP headers or all of them with the wildcard `"*"`.
|
||||
|
||||
```Python hl_lines="2 6 7 8 9 10 11 13 14 15 16 17 18 19"
|
||||
```Python hl_lines="2 6-11 13-19"
|
||||
{!../../../docs_src/cors/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ You can connect the debugger in your editor, for example with Visual Studio Code
|
||||
|
||||
In your FastAPI application, import and run `uvicorn` directly:
|
||||
|
||||
```Python hl_lines="1 15"
|
||||
```Python hl_lines="1 15"
|
||||
{!../../../docs_src/debugging/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ Before diving deeper into the **Dependency Injection** system, let's upgrade the
|
||||
|
||||
In the previous example, we were returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -69,21 +69,21 @@ If you pass a "callable" as a dependency in **FastAPI**, it will analyze the par
|
||||
|
||||
That also applies to callables with no parameters at all. The same as it would be for *path operation functions* with no parameters.
|
||||
|
||||
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParameters`:
|
||||
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParams`:
|
||||
|
||||
```Python hl_lines="9 10 11 12 13"
|
||||
```Python hl_lines="11-15"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
Pay attention to the `__init__` method used to create the instance of the class:
|
||||
|
||||
```Python hl_lines="10"
|
||||
```Python hl_lines="12"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
...it has the same parameters as our previous `common_parameters`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
```Python hl_lines="8"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -101,15 +101,15 @@ In both cases the data will be converted, validated, documented on the OpenAPI s
|
||||
|
||||
Now you can declare your dependency using this class.
|
||||
|
||||
And as when **FastAPI** calls that class the value that will be passed as `commons` to your function will be an "instance" of the class, you can declare that parameter `commons` to be of type of the class, `CommonQueryParams`.
|
||||
|
||||
```Python hl_lines="17"
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
**FastAPI** calls the `CommonQueryParams` class. This creates an "instance" of that class and the instance will be passed as the parameter `commons` to your function.
|
||||
|
||||
## Type annotation vs `Depends`
|
||||
|
||||
In the code above, you are declaring `commons` as:
|
||||
Notice how we write `CommonQueryParams` twice in the above code:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
@@ -143,7 +143,7 @@ commons = Depends(CommonQueryParams)
|
||||
|
||||
..as in:
|
||||
|
||||
```Python hl_lines="17"
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -175,17 +175,17 @@ commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
commons: CommonQueryParams = Depends()
|
||||
```
|
||||
|
||||
So, you can declare the dependency as the type of the variable, and use `Depends()` as the "default" value (the value after the `=`) for that function's parameter, without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
|
||||
You declare the dependency as the type of the parameter, and you use `Depends()` as its "default" value (that after the `=`) for that function's parameter, without any parameter in `Depends()`, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
|
||||
|
||||
So, the same example would look like:
|
||||
The same example would then look like:
|
||||
|
||||
```Python hl_lines="17"
|
||||
```Python hl_lines="19"
|
||||
{!../../../docs_src/dependencies/tutorial004.py!}
|
||||
```
|
||||
|
||||
...and **FastAPI** will know what to do.
|
||||
|
||||
!!! tip
|
||||
If all that seems more confusing than helpful, disregard it, you don't *need* it.
|
||||
|
||||
If that seems more confusing than helpful, disregard it, you don't *need* it.
|
||||
|
||||
It is just a shortcut. Because **FastAPI** cares about helping you minimize code repetition.
|
||||
|
||||
@@ -25,7 +25,7 @@ These dependencies will be executed/solved the same way normal dependencies. But
|
||||
|
||||
Using these `dependencies` in the *path operation decorator* you can make sure they are executed while avoiding editor/tooling errors.
|
||||
|
||||
It might also help avoiding confusion for new developers that see an un-used parameter in your code and could think it's unnecessary.
|
||||
It might also help avoid confusion for new developers that see an unused parameter in your code and could think it's unnecessary.
|
||||
|
||||
## Dependencies errors and return values
|
||||
|
||||
@@ -35,7 +35,7 @@ You can use the same dependency *functions* you use normally.
|
||||
|
||||
They can declare request requirements (like headers) or other sub-dependencies:
|
||||
|
||||
```Python hl_lines="6 11"
|
||||
```Python hl_lines="6 11"
|
||||
{!../../../docs_src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
@@ -43,7 +43,7 @@ They can declare request requirements (like headers) or other sub-dependencies:
|
||||
|
||||
These dependencies can `raise` exceptions, the same as normal dependencies:
|
||||
|
||||
```Python hl_lines="8 13"
|
||||
```Python hl_lines="8 13"
|
||||
{!../../../docs_src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
@@ -53,7 +53,7 @@ And they can return values or not, the values won't be used.
|
||||
|
||||
So, you can re-use a normal dependency (that returns a value) you already use somewhere else, and even though the value won't be used, the dependency will be executed:
|
||||
|
||||
```Python hl_lines="9 14"
|
||||
```Python hl_lines="9 14"
|
||||
{!../../../docs_src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ For example, you could use this to create a database session and close it after
|
||||
|
||||
Only the code prior to and including the `yield` statement is executed before sending a response:
|
||||
|
||||
```Python hl_lines="2 3 4"
|
||||
```Python hl_lines="2-4"
|
||||
{!../../../docs_src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -44,7 +44,7 @@ The yielded value is what is injected into *path operations* and other dependenc
|
||||
|
||||
The code following the `yield` statement is executed after the response has been delivered:
|
||||
|
||||
```Python hl_lines="5 6"
|
||||
```Python hl_lines="5-6"
|
||||
{!../../../docs_src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -63,7 +63,7 @@ So, you can look for that specific exception inside the dependency with `except
|
||||
|
||||
In the same way, you can use `finally` to make sure the exit steps are executed, no matter if there was an exception or not.
|
||||
|
||||
```Python hl_lines="3 5"
|
||||
```Python hl_lines="3 5"
|
||||
{!../../../docs_src/dependencies/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -75,7 +75,7 @@ You can have sub-dependencies and "trees" of sub-dependencies of any size and sh
|
||||
|
||||
For example, `dependency_c` can have a dependency on `dependency_b`, and `dependency_b` on `dependency_a`:
|
||||
|
||||
```Python hl_lines="4 12 20"
|
||||
```Python hl_lines="4 12 20"
|
||||
{!../../../docs_src/dependencies/tutorial008.py!}
|
||||
```
|
||||
|
||||
@@ -85,7 +85,7 @@ In this case `dependency_c`, to execute its exit code, needs the value from `dep
|
||||
|
||||
And, in turn, `dependency_b` needs the value from `dependency_a` (here named `dep_a`) to be available for its exit code.
|
||||
|
||||
```Python hl_lines="16 17 24 25"
|
||||
```Python hl_lines="16-17 24-25"
|
||||
{!../../../docs_src/dependencies/tutorial008.py!}
|
||||
```
|
||||
|
||||
@@ -207,7 +207,7 @@ In Python, you can create Context Managers by <a href="https://docs.python.org/3
|
||||
You can also use them inside of **FastAPI** dependencies with `yield` by using
|
||||
`with` or `async with` statements inside of the dependency function:
|
||||
|
||||
```Python hl_lines="1 2 3 4 5 6 7 8 9 13"
|
||||
```Python hl_lines="1-9 13"
|
||||
{!../../../docs_src/dependencies/tutorial010.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ Let's first focus on the dependency.
|
||||
|
||||
It is just a function that can take all the same parameters that a *path operation function* can take:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
```Python hl_lines="8-9"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -39,7 +39,7 @@ That's it.
|
||||
|
||||
**2 lines**.
|
||||
|
||||
And it has the same shape and structure that all your *path operation functions*.
|
||||
And it has the same shape and structure that all your *path operation functions* have.
|
||||
|
||||
You can think of it as a *path operation function* without the "decorator" (without the `@app.get("/some-path")`).
|
||||
|
||||
@@ -55,7 +55,7 @@ And then it just returns a `dict` containing those values.
|
||||
|
||||
### Import `Depends`
|
||||
|
||||
```Python hl_lines="1"
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -63,7 +63,7 @@ And then it just returns a `dict` containing those values.
|
||||
|
||||
The same way you use `Body`, `Query`, etc. with your *path operation function* parameters, use `Depends` with a new parameter:
|
||||
|
||||
```Python hl_lines="11 16"
|
||||
```Python hl_lines="13 18"
|
||||
{!../../../docs_src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -123,10 +123,9 @@ So, the interactive docs will have all the information from these dependencies t
|
||||
|
||||
<img src="/img/tutorial/dependencies/image01.png">
|
||||
|
||||
|
||||
## Simple usage
|
||||
|
||||
If you look at it, *path operation functions* are declared to be used whenever a *path* and *operation* matches, and then **FastAPI** takes care of calling the function with the correct parameters and use the response.
|
||||
If you look at it, *path operation functions* are declared to be used whenever a *path* and *operation* matches, and then **FastAPI** takes care of calling the function with the correct parameters, extracting the data from the request.
|
||||
|
||||
Actually, all (or most) of the web frameworks work in this same way.
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ They can be as **deep** as you need them to be.
|
||||
|
||||
You could create a first dependency ("dependable") like:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
```Python hl_lines="8-9"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -22,7 +22,7 @@ This is quite simple (not very useful), but will help us focus on how the sub-de
|
||||
|
||||
Then you can create another dependency function (a "dependable") that at the same time declares a dependency of its own (so it is a "dependant" too):
|
||||
|
||||
```Python hl_lines="11"
|
||||
```Python hl_lines="13"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -31,13 +31,13 @@ Let's focus on the parameters declared:
|
||||
* Even though this function is a dependency ("dependable") itself, it also declares another dependency (it "depends" on something else).
|
||||
* It depends on the `query_extractor`, and assigns the value returned by it to the parameter `q`.
|
||||
* It also declares an optional `last_query` cookie, as a `str`.
|
||||
* Let's imagine that if the user didn't provide any query `q`, we just use the last query used, that we had saved to a cookie before.
|
||||
* If the user didn't provide any query `q`, we use the last query used, which we saved to a cookie before.
|
||||
|
||||
### Use the dependency
|
||||
|
||||
Then we can use the dependency with:
|
||||
|
||||
```Python hl_lines="19"
|
||||
```Python hl_lines="21"
|
||||
{!../../../docs_src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ You can use `jsonable_encoder` for that.
|
||||
|
||||
It receives an object, like a Pydantic model, and returns a JSON compatible version:
|
||||
|
||||
```Python hl_lines="4 21"
|
||||
```Python hl_lines="5 22"
|
||||
{!../../../docs_src/encoder/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -49,17 +49,18 @@ Here are some of the additional data types you can use:
|
||||
* `Decimal`:
|
||||
* Standard Python `Decimal`.
|
||||
* In requests and responses, handled the same as a `float`.
|
||||
* You can check all the valid pydantic data types here: <a href="https://pydantic-docs.helpmanual.io/usage/types" class="external-link" target="_blank">Pydantic data types</a>.
|
||||
|
||||
## Example
|
||||
|
||||
Here's an example *path operation* with parameters using some of the above types.
|
||||
|
||||
```Python hl_lines="1 2 11 12 13 14 15"
|
||||
```Python hl_lines="1 3 12-16"
|
||||
{!../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
Note that the parameters inside the function have their natural data type, and you can, for example, perform normal date manipulations, like:
|
||||
|
||||
```Python hl_lines="17 18"
|
||||
```Python hl_lines="18-19"
|
||||
{!../../../docs_src/extra_data_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -17,7 +17,7 @@ This is especially the case for user models, because:
|
||||
|
||||
Here's a general idea of how the models could look like with their password fields and the places where they are used:
|
||||
|
||||
```Python hl_lines="7 9 14 20 22 27 28 31 32 33 38 39"
|
||||
```Python hl_lines="9 11 16 22 24 29-30 33-35 40-41"
|
||||
{!../../../docs_src/extra_models/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -150,7 +150,7 @@ All the data conversion, validation, documentation, etc. will still work as norm
|
||||
|
||||
That way, we can declare just the differences between the models (with plaintext `password`, with `hashed_password` and without password):
|
||||
|
||||
```Python hl_lines="7 13 14 17 18 21 22"
|
||||
```Python hl_lines="9 15-16 19-20 23-24"
|
||||
{!../../../docs_src/extra_models/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -162,7 +162,10 @@ It will be defined in OpenAPI with `anyOf`.
|
||||
|
||||
To do that, use the standard Python type hint <a href="https://docs.python.org/3/library/typing.html#typing.Union" class="external-link" target="_blank">`typing.Union`</a>:
|
||||
|
||||
```Python hl_lines="1 14 15 18 19 20 33"
|
||||
!!! note
|
||||
When defining a <a href="https://pydantic-docs.helpmanual.io/usage/types/#unions" class="external-link" target="_blank">`Union`</a>, include the most specific type first, followed by the less specific type. In the example below, the more specific `PlaneItem` comes before `CarItem` in `Union[PlaneItem, CarItem]`.
|
||||
|
||||
```Python hl_lines="1 14-15 18-20 33"
|
||||
{!../../../docs_src/extra_models/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -172,7 +175,7 @@ The same way, you can declare responses of lists of objects.
|
||||
|
||||
For that, use the standard Python `typing.List`:
|
||||
|
||||
```Python hl_lines="1 20"
|
||||
```Python hl_lines="1 20"
|
||||
{!../../../docs_src/extra_models/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -184,7 +187,7 @@ This is useful if you don't know the valid field/attribute names (that would be
|
||||
|
||||
In this case, you can use `typing.Dict`:
|
||||
|
||||
```Python hl_lines="1 8"
|
||||
```Python hl_lines="1 8"
|
||||
{!../../../docs_src/extra_models/tutorial005.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ In this example, when the client request an item by an ID that doesn't exist, ra
|
||||
|
||||
### The resulting response
|
||||
|
||||
If the client requests `http://example.com/items/foo` (an `item_id` `"foo"`), he will receive an HTTP status code of 200, and a JSON response of:
|
||||
If the client requests `http://example.com/items/foo` (an `item_id` `"foo"`), that client will receive an HTTP status code of 200, and a JSON response of:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -55,7 +55,7 @@ If the client requests `http://example.com/items/foo` (an `item_id` `"foo"`), he
|
||||
}
|
||||
```
|
||||
|
||||
But if the client requests `http://example.com/items/bar` (a non-existent `item_id` `"bar"`), he will receive an HTTP status code of 404 (the "not found" error), and a JSON response of:
|
||||
But if the client requests `http://example.com/items/bar` (a non-existent `item_id` `"bar"`), that client will receive an HTTP status code of 404 (the "not found" error), and a JSON response of:
|
||||
|
||||
```JSON
|
||||
{
|
||||
@@ -92,7 +92,7 @@ And you want to handle this exception globally with FastAPI.
|
||||
|
||||
You could add a custom exception handler with `@app.exception_handler()`:
|
||||
|
||||
```Python hl_lines="5 6 7 13 14 15 16 17 18 24"
|
||||
```Python hl_lines="5-7 13-18 24"
|
||||
{!../../../docs_src/handling_errors/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -129,7 +129,7 @@ To override it, import the `RequestValidationError` and use it with `@app.except
|
||||
|
||||
The exception handler will receive a `Request` and the exception.
|
||||
|
||||
```Python hl_lines="2 14 15 16"
|
||||
```Python hl_lines="2 14-16"
|
||||
{!../../../docs_src/handling_errors/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -179,7 +179,7 @@ The same way, you can override the `HTTPException` handler.
|
||||
|
||||
For example, you could want to return a plain text response instead of JSON for these errors:
|
||||
|
||||
```Python hl_lines="3 4 9 10 11 22"
|
||||
```Python hl_lines="3-4 9-11 22"
|
||||
{!../../../docs_src/handling_errors/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -209,13 +209,12 @@ Now try sending an invalid item like:
|
||||
|
||||
You will receive a response telling you that the data is invalid containing the received body:
|
||||
|
||||
```JSON hl_lines="13 14 15 16"
|
||||
```JSON hl_lines="12-15"
|
||||
{
|
||||
"detail": [
|
||||
{
|
||||
"loc": [
|
||||
"body",
|
||||
"item",
|
||||
"size"
|
||||
],
|
||||
"msg": "value is not a valid integer",
|
||||
@@ -257,7 +256,7 @@ You could also just want to use the exception somehow, but then use the same def
|
||||
|
||||
You can import and re-use the default exception handlers from `fastapi.exception_handlers`:
|
||||
|
||||
```Python hl_lines="2 3 4 5 15 21"
|
||||
```Python hl_lines="2-5 15 21"
|
||||
{!../../../docs_src/handling_errors/tutorial006.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ You can define Header parameters the same way you define `Query`, `Path` and `Co
|
||||
|
||||
First import `Header`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ Then declare the header parameters using the same structure as with `Path`, `Que
|
||||
|
||||
The first value is the default value, you can pass all the extra validation or annotation parameters:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/header_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -44,7 +44,7 @@ So, you can use `user_agent` as you normally would in Python code, instead of ne
|
||||
|
||||
If for some reason you need to disable automatic conversion of underscores to hyphens, set the parameter `convert_underscores` of `Header` to `False`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/header_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ You can set the:
|
||||
|
||||
To set them, use the parameters `title`, `description`, and `version`:
|
||||
|
||||
```Python hl_lines="4 5 6"
|
||||
```Python hl_lines="4-6"
|
||||
{!../../../docs_src/metadata/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -41,7 +41,7 @@ Let's try that in an example with tags for `users` and `items`.
|
||||
|
||||
Create metadata for your tags and pass it to the `openapi_tags` parameter:
|
||||
|
||||
```Python hl_lines="3 4 5 6 7 8 9 10 11 12 13 14 15 16 18"
|
||||
```Python hl_lines="3-16 18"
|
||||
{!../../../docs_src/metadata/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ The middleware function receives:
|
||||
* Then it returns the `response` generated by the corresponding *path operation*.
|
||||
* You can then modify further the `response` before returning it.
|
||||
|
||||
```Python hl_lines="8 9 11 14"
|
||||
```Python hl_lines="8-9 11 14"
|
||||
{!../../../docs_src/middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -50,7 +50,7 @@ And also after the `response` is generated, before returning it.
|
||||
|
||||
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
|
||||
|
||||
```Python hl_lines="10 12 13"
|
||||
```Python hl_lines="10 12-13"
|
||||
{!../../../docs_src/middleware/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ That status code will be used in the response and will be added to the OpenAPI s
|
||||
|
||||
You can add tags to your *path operation*, pass the parameter `tags` with a `list` of `str` (commonly just one `str`):
|
||||
|
||||
```Python hl_lines="17 22 27"
|
||||
```Python hl_lines="17 22 27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -40,7 +40,7 @@ They will be added to the OpenAPI schema and used by the automatic documentation
|
||||
|
||||
You can add a `summary` and `description`:
|
||||
|
||||
```Python hl_lines="20 21"
|
||||
```Python hl_lines="20-21"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -50,7 +50,7 @@ As descriptions tend to be long and cover multiple lines, you can declare the *p
|
||||
|
||||
You can write <a href="https://en.wikipedia.org/wiki/Markdown" class="external-link" target="_blank">Markdown</a> in the docstring, it will be interpreted and displayed correctly (taking into account docstring indentation).
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24 25 26 27"
|
||||
```Python hl_lines="19-27"
|
||||
{!../../../docs_src/path_operation_configuration/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ The same way you can declare more validations and metadata for query parameters
|
||||
|
||||
First, import `Path` from `fastapi`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -16,7 +16,7 @@ You can declare all the same parameters as for `Query`.
|
||||
|
||||
For example, to declare a `title` metadata value for the path parameter `item_id` you can type:
|
||||
|
||||
```Python hl_lines="8"
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/path_params_numeric_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
You can declare path "parameters" or "variables" with the same syntax used by Python format strings:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
```Python hl_lines="6-7"
|
||||
{!../../../docs_src/path_params/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -109,7 +109,7 @@ And then you can also have a path `/users/{user_id}` to get data about a specifi
|
||||
|
||||
Because *path operations* are evaluated in order, you need to make sure that the path for `/users/me` is declared before the one for `/users/{user_id}`:
|
||||
|
||||
```Python hl_lines="6 11"
|
||||
```Python hl_lines="6 11"
|
||||
{!../../../docs_src/path_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -127,7 +127,7 @@ By inheriting from `str` the API docs will be able to know that the values must
|
||||
|
||||
Then create class attributes with fixed values, which will be the available valid values:
|
||||
|
||||
```Python hl_lines="1 6 7 8 9"
|
||||
```Python hl_lines="1 6-9"
|
||||
{!../../../docs_src/path_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -4,11 +4,16 @@
|
||||
|
||||
Let's take this application as example:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial001.py!}
|
||||
```
|
||||
|
||||
The query parameter `q` is of type `str`, and by default is `None`, so it is optional.
|
||||
The query parameter `q` is of type `Optional[str]`, that means that it's of type `str` but could also be `None`, and indeed, the default value is `None`, so FastAPI will know it's not required.
|
||||
|
||||
!!! note
|
||||
FastAPI will know that the value of `q` is not required because of the default value `= None`.
|
||||
|
||||
The `Optional` in `Optional[str]` is not used by FastAPI, but will allow your editor to give you better support and detect errors.
|
||||
|
||||
## Additional validation
|
||||
|
||||
@@ -18,7 +23,7 @@ We are going to enforce that even though `q` is optional, whenever it is provide
|
||||
|
||||
To achieve that, first import `Query` from `fastapi`:
|
||||
|
||||
```Python hl_lines="1"
|
||||
```Python hl_lines="3"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -26,7 +31,7 @@ To achieve that, first import `Query` from `fastapi`:
|
||||
|
||||
And now use it as the default value of your parameter, setting the parameter `max_length` to 50:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -35,18 +40,35 @@ As we have to replace the default value `None` with `Query(None)`, the first par
|
||||
So:
|
||||
|
||||
```Python
|
||||
q: str = Query(None)
|
||||
q: Optional[str] = Query(None)
|
||||
```
|
||||
|
||||
...makes the parameter optional, the same as:
|
||||
|
||||
```Python
|
||||
q: str = None
|
||||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
But it declares it explicitly as being a query parameter.
|
||||
|
||||
And then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings:
|
||||
!!! info
|
||||
Have in mind that FastAPI cares about the part:
|
||||
|
||||
```Python
|
||||
= None
|
||||
```
|
||||
|
||||
or the:
|
||||
|
||||
```Python
|
||||
= Query(None)
|
||||
```
|
||||
|
||||
and will use that `None` to detect that the query parameter is not required.
|
||||
|
||||
The `Optional` part is only to allow your editor to provide better support.
|
||||
|
||||
Then, we can pass more parameters to `Query`. In this case, the `max_length` parameter that applies to strings:
|
||||
|
||||
```Python
|
||||
q: str = Query(None, max_length=50)
|
||||
@@ -58,7 +80,7 @@ This will validate the data, show a clear error when the data is not valid, and
|
||||
|
||||
You can also add a parameter `min_length`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -66,7 +88,7 @@ You can also add a parameter `min_length`:
|
||||
|
||||
You can define a <abbr title="A regular expression, regex or regexp is a sequence of characters that define a search pattern for strings.">regular expression</abbr> that the parameter should match:
|
||||
|
||||
```Python hl_lines="8"
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -104,13 +126,13 @@ q: str
|
||||
instead of:
|
||||
|
||||
```Python
|
||||
q: str = None
|
||||
q: Optional[str] = None
|
||||
```
|
||||
|
||||
But we are now declaring it with `Query`, for example like:
|
||||
|
||||
```Python
|
||||
q: str = Query(None, min_length=3)
|
||||
q: Optional[str] = Query(None, min_length=3)
|
||||
```
|
||||
|
||||
So, when you need to declare a value as required while using `Query`, you can use `...` as the first argument:
|
||||
@@ -120,7 +142,7 @@ So, when you need to declare a value as required while using `Query`, you can us
|
||||
```
|
||||
|
||||
!!! info
|
||||
If you hadn't seen that `...` before: it is a a special single value, it is <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">part of Python and is called "Ellipsis"</a>.
|
||||
If you hadn't seen that `...` before: it is a special single value, it is <a href="https://docs.python.org/3/library/constants.html#Ellipsis" class="external-link" target="_blank">part of Python and is called "Ellipsis"</a>.
|
||||
|
||||
This will let **FastAPI** know that this parameter is required.
|
||||
|
||||
@@ -211,13 +233,13 @@ That information will be included in the generated OpenAPI and used by the docum
|
||||
|
||||
You can add a `title`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial007.py!}
|
||||
```
|
||||
|
||||
And a `description`:
|
||||
|
||||
```Python hl_lines="11"
|
||||
```Python hl_lines="13"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial008.py!}
|
||||
```
|
||||
|
||||
@@ -239,7 +261,7 @@ But you still need it to be exactly `item-query`...
|
||||
|
||||
Then you can declare an `alias`, and that alias is what will be used to find the parameter value:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial009.py!}
|
||||
```
|
||||
|
||||
@@ -251,7 +273,7 @@ You have to leave it there a while because there are clients using it, but you w
|
||||
|
||||
Then pass the parameter `deprecated=True` to `Query`:
|
||||
|
||||
```Python hl_lines="16"
|
||||
```Python hl_lines="18"
|
||||
{!../../../docs_src/query_params_str_validations/tutorial010.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ The parameter values in your function will be:
|
||||
|
||||
The same way, you can declare optional query parameters, by setting their default to `None`:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -72,11 +72,16 @@ In this case, the function parameter `q` will be optional, and will be `None` by
|
||||
!!! check
|
||||
Also notice that **FastAPI** is smart enough to notice that the path parameter `item_id` is a path parameter and `q` is not, so, it's a query parameter.
|
||||
|
||||
!!! note
|
||||
FastAPI will know that `q` is optional because of the `= None`.
|
||||
|
||||
The `Optional` in `Optional[str]` is not used by FastAPI (FastAPI will only use the `str` part), but the `Optional[str]` will let your editor help you finding errors in your code.
|
||||
|
||||
## Query parameter type conversion
|
||||
|
||||
You can also declare `bool` types, and they will be converted:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -121,7 +126,7 @@ And you don't have to declare them in any specific order.
|
||||
|
||||
They will be detected by name:
|
||||
|
||||
```Python hl_lines="6 8"
|
||||
```Python hl_lines="8 10"
|
||||
{!../../../docs_src/query_params/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -133,7 +138,7 @@ If you don't want to add a specific value but just make it optional, set the def
|
||||
|
||||
But when you want to make a query parameter required, you can just not declare any default value:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
```Python hl_lines="6-7"
|
||||
{!../../../docs_src/query_params/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -179,7 +184,7 @@ http://127.0.0.1:8000/items/foo-item?needy=sooooneedy
|
||||
|
||||
And of course, you can define some parameters as required, some as having a default value, and some entirely optional:
|
||||
|
||||
```Python hl_lines="7"
|
||||
```Python hl_lines="10"
|
||||
{!../../../docs_src/query_params/tutorial006.py!}
|
||||
```
|
||||
|
||||
@@ -191,36 +196,3 @@ In this case, there are 3 query parameters:
|
||||
|
||||
!!! tip
|
||||
You could also use `Enum`s the same way as with [Path Parameters](path-params.md#predefined-values){.internal-link target=_blank}.
|
||||
|
||||
## Optional type declarations
|
||||
|
||||
!!! warning
|
||||
This might be an advanced use case.
|
||||
|
||||
You might want to skip it.
|
||||
|
||||
If you are using `mypy` it could complain with type declarations like:
|
||||
|
||||
```Python
|
||||
limit: int = None
|
||||
```
|
||||
|
||||
With an error like:
|
||||
|
||||
```
|
||||
Incompatible types in assignment (expression has type "None", variable has type "int")
|
||||
```
|
||||
|
||||
In those cases you can use `Optional` to tell `mypy` that the value could be `None`, like:
|
||||
|
||||
```Python
|
||||
from typing import Optional
|
||||
|
||||
limit: Optional[int] = None
|
||||
```
|
||||
|
||||
In a *path operation* that could look like:
|
||||
|
||||
```Python hl_lines="9"
|
||||
{!../../../docs_src/query_params/tutorial007.py!}
|
||||
```
|
||||
|
||||
@@ -121,7 +121,7 @@ They would be associated to the same "form field" sent using "form data".
|
||||
|
||||
To use that, declare a `List` of `bytes` or `UploadFile`:
|
||||
|
||||
```Python hl_lines="10 15"
|
||||
```Python hl_lines="10 15"
|
||||
{!../../../docs_src/request_files/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -35,13 +35,13 @@ But most importantly:
|
||||
|
||||
Here we are declaring a `UserIn` model, it will contain a plaintext password:
|
||||
|
||||
```Python hl_lines="7 9"
|
||||
```Python hl_lines="9 11"
|
||||
{!../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
And we are using this model to declare our input and the same model to declare our output:
|
||||
|
||||
```Python hl_lines="15 16"
|
||||
```Python hl_lines="17-18"
|
||||
{!../../../docs_src/response_model/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -58,19 +58,19 @@ But if we use the same model for another *path operation*, we could be sending o
|
||||
|
||||
We can instead create an input model with the plaintext password and an output model without it:
|
||||
|
||||
```Python hl_lines="7 9 14"
|
||||
```Python hl_lines="9 11 16"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
Here, even though our *path operation function* is returning the same input user that contains the password:
|
||||
|
||||
```Python hl_lines="22"
|
||||
```Python hl_lines="24"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
...we declared the `response_model` to be our model `UserOut`, that doesn't include the password:
|
||||
|
||||
```Python hl_lines="20"
|
||||
```Python hl_lines="22"
|
||||
{!../../../docs_src/response_model/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -90,13 +90,13 @@ And both models will be used for the interactive API documentation:
|
||||
|
||||
Your response model could have default values, like:
|
||||
|
||||
```Python hl_lines="11 13 14"
|
||||
```Python hl_lines="11 13-14"
|
||||
{!../../../docs_src/response_model/tutorial004.py!}
|
||||
```
|
||||
|
||||
* `description: str = None` has a default of `None`.
|
||||
* `description: Optional[str] = None` has a default of `None`.
|
||||
* `tax: float = 10.5` has a default of `10.5`.
|
||||
* `tags: List[str] = []` has a default of an empty list: `[]`.
|
||||
* `tags: List[str] = []` as a default of an empty list: `[]`.
|
||||
|
||||
but you might want to omit them from the result if they were not actually stored.
|
||||
|
||||
@@ -136,7 +136,7 @@ So, if you send a request to that *path operation* for the item with ID `foo`, t
|
||||
|
||||
But if your data has values for the model's fields with default values, like the item with ID `bar`:
|
||||
|
||||
```Python hl_lines="3 5"
|
||||
```Python hl_lines="3 5"
|
||||
{
|
||||
"name": "Bar",
|
||||
"description": "The bartenders",
|
||||
@@ -151,7 +151,7 @@ they will be included in the response.
|
||||
|
||||
If the data has the same values as the default ones, like the item with ID `baz`:
|
||||
|
||||
```Python hl_lines="3 5 6"
|
||||
```Python hl_lines="3 5-6"
|
||||
{
|
||||
"name": "Baz",
|
||||
"description": None,
|
||||
@@ -183,7 +183,9 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
|
||||
|
||||
This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you use `response_model_include` or `response_model_exclude` to omit some attributes.
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
This also applies to `response_model_by_alias` that works similarly.
|
||||
|
||||
```Python hl_lines="31 37"
|
||||
{!../../../docs_src/response_model/tutorial005.py!}
|
||||
```
|
||||
|
||||
@@ -196,7 +198,7 @@ This can be used as a quick shortcut if you have only one Pydantic model and wan
|
||||
|
||||
If you forget to use a `set` and use a `list` or `tuple` instead, FastAPI will still convert it to a `set` and it will work correctly:
|
||||
|
||||
```Python hl_lines="29 35"
|
||||
```Python hl_lines="31 37"
|
||||
{!../../../docs_src/response_model/tutorial006.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -71,7 +71,7 @@ But you don't have to memorize what each of these codes mean.
|
||||
|
||||
You can use the convenience variables from `fastapi.status`.
|
||||
|
||||
```Python hl_lines="1 6"
|
||||
```Python hl_lines="1 6"
|
||||
{!../../../docs_src/response_status_code/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ There are several ways you can declare extra JSON Schema information.
|
||||
|
||||
You can declare an example for a Pydantic model using `Config` and `schema_extra`, as described in <a href="https://pydantic-docs.helpmanual.io/usage/schema/#schema-customization" class="external-link" target="_blank">Pydantic's docs: Schema customization</a>:
|
||||
|
||||
```Python hl_lines="13 14 15 16 17 18 19 20 21"
|
||||
```Python hl_lines="15-23"
|
||||
{!../../../docs_src/schema_extra_example/tutorial001.py!}
|
||||
```
|
||||
|
||||
@@ -20,7 +20,7 @@ That extra info will be added as-is to the output JSON Schema.
|
||||
|
||||
In `Field`, `Path`, `Query`, `Body` and others you'll see later, you can also declare extra info for the JSON Schema by passing any other arbitrary arguments to the function, for example, to add an `example`:
|
||||
|
||||
```Python hl_lines="2 8 9 10 11"
|
||||
```Python hl_lines="4 10-13"
|
||||
{!../../../docs_src/schema_extra_example/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -33,7 +33,7 @@ The same way you can pass extra info to `Field`, you can do the same with `Path`
|
||||
|
||||
For example, you can pass an `example` for a body request to `Body`:
|
||||
|
||||
```Python hl_lines="19 20 21 22 23 24"
|
||||
```Python hl_lines="21-26"
|
||||
{!../../../docs_src/schema_extra_example/tutorial003.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -85,12 +85,12 @@ But in this case, the same **FastAPI** application will handle the API and the a
|
||||
|
||||
So, let's review it from that simplified point of view:
|
||||
|
||||
* The user types his `username` and `password` in the frontend, and hits `Enter`.
|
||||
* The frontend (running in the user's browser) sends that `username` and `password` to a specific URL in our API.
|
||||
* The API checks that `username` and `password`, and responds with a "token".
|
||||
* The user types the `username` and `password` in the frontend, and hits `Enter`.
|
||||
* The frontend (running in the user's browser) sends that `username` and `password` to a specific URL in our API (declared with `tokenUrl="token"`).
|
||||
* The API checks that `username` and `password`, and responds with a "token" (we haven't implemented any of this yet).
|
||||
* A "token" is just a string with some content that we can use later to verify this user.
|
||||
* Normally, a token is set to expire after some time.
|
||||
* So, the user will have to login again at some point later.
|
||||
* So, the user will have to log in again at some point later.
|
||||
* And if the token is stolen, the risk is less. It is not like a permanent key that will work forever (in most of the cases).
|
||||
* The frontend stores that token temporarily somewhere.
|
||||
* The user clicks in the frontend to go to another section of the frontend web app.
|
||||
@@ -103,7 +103,7 @@ So, let's review it from that simplified point of view:
|
||||
|
||||
**FastAPI** provides several tools, at different levels of abstraction, to implement these security features.
|
||||
|
||||
In this example we are going to use **OAuth2**, with the **Password** flow, using a **Bearer** token.
|
||||
In this example we are going to use **OAuth2**, with the **Password** flow, using a **Bearer** token. We do that using the `OAuth2PasswordBearer` class.
|
||||
|
||||
!!! info
|
||||
A "bearer" token is not the only option.
|
||||
@@ -114,13 +114,22 @@ In this example we are going to use **OAuth2**, with the **Password** flow, usin
|
||||
|
||||
In that case, **FastAPI** also provides you with the tools to build it.
|
||||
|
||||
`OAuth2PasswordBearer` is a class that we create passing a parameter of the URL in where the client (the frontend running in the user's browser) can use to send the `username` and `password` and get a token.
|
||||
When we create an instance of the `OAuth2PasswordBearer` class we pass in the `tokenUrl` parameter. This parameter contains the URL that the client (the frontend running in the user's browser) will use to send the `username` and `password` in order to get a token.
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!../../../docs_src/security/tutorial001.py!}
|
||||
```
|
||||
|
||||
It doesn't create that endpoint / *path operation*, but declares that that URL is the one that the client should use to get the token. That information is used in OpenAPI, and then in the interactive API documentation systems.
|
||||
!!! tip
|
||||
here `tokenUrl="token"` refers to a relative URL `token` that we haven't created yet. As it's a relative URL, it's equivalent to `./token`.
|
||||
|
||||
Because we are using a relative URL, if your API was located at `https://example.com/`, then it would refer to `https://example.com/token`. But if your API was located at `https://example.com/api/v1/`, then it would refer to `https://example.com/api/v1/token`.
|
||||
|
||||
Using a relative URL is important to make sure your application keeps working even in an advanced use case like [Behind a Proxy](../../advanced/behind-a-proxy.md){.internal-link target=_blank}.
|
||||
|
||||
This parameter doesn't create that endpoint / *path operation*, but declares that the URL `/token` will be the one that the client should use to get the token. That information is used in OpenAPI, and then in the interactive API documentation systems.
|
||||
|
||||
We will soon also create the actual path operation.
|
||||
|
||||
!!! info
|
||||
If you are a very strict "Pythonista" you might dislike the style of the parameter name `tokenUrl` instead of `token_url`.
|
||||
|
||||
@@ -16,7 +16,7 @@ First, let's create a Pydantic user model.
|
||||
|
||||
The same way we use Pydantic to declare bodies, we can use it anywhere else:
|
||||
|
||||
```Python hl_lines="5 12 13 14 15 16"
|
||||
```Python hl_lines="5 12-16"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -38,7 +38,7 @@ The same as we were doing before in the *path operation* directly, our new depen
|
||||
|
||||
`get_current_user` will use a (fake) utility function we created, that takes a token as a `str` and returns our Pydantic `User` model:
|
||||
|
||||
```Python hl_lines="19 20 21 22 26 27"
|
||||
```Python hl_lines="19-22 26-27"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
@@ -98,7 +98,7 @@ And all of them (or any portion of them that you want) can take the advantage of
|
||||
|
||||
And all these thousands of *path operations* can be as small as 3 lines:
|
||||
|
||||
```Python hl_lines="30 31 32"
|
||||
```Python hl_lines="30-32"
|
||||
{!../../../docs_src/security/tutorial002.py!}
|
||||
```
|
||||
|
||||
|
||||
@@ -20,26 +20,35 @@ It is not encrypted, so, anyone could recover the information from the contents.
|
||||
|
||||
But it's signed. So, when you receive a token that you emitted, you can verify that you actually emitted it.
|
||||
|
||||
That way, you can create a token with an expiration of, let's say, 1 week. And then when the user comes back the next day with the token, you know she/he is still signed into your system.
|
||||
That way, you can create a token with an expiration of, let's say, 1 week. And then when the user comes back the next day with the token, you know that user is still logged in to your system.
|
||||
|
||||
And after a week, the token will be expired and the user will not be authorized and will have to sign in again to get a new token. And if the user (or a third party) tried to modify the token to change the expiration, you would be able to discover it, because the signatures would not match.
|
||||
After a week, the token will be expired and the user will not be authorized and will have to sign in again to get a new token. And if the user (or a third party) tried to modify the token to change the expiration, you would be able to discover it, because the signatures would not match.
|
||||
|
||||
If you want to play with JWT tokens and see how they work, check <a href="https://jwt.io/" class="external-link" target="_blank">https://jwt.io</a>.
|
||||
|
||||
## Install `PyJWT`
|
||||
## Install `python-jose`
|
||||
|
||||
We need to install `PyJWT` to generate and verify the JWT tokens in Python:
|
||||
We need to install `python-jose` to generate and verify the JWT tokens in Python:
|
||||
|
||||
<div class="termy">
|
||||
|
||||
```console
|
||||
$ pip install pyjwt
|
||||
$ pip install python-jose[cryptography]
|
||||
|
||||
---> 100%
|
||||
```
|
||||
|
||||
</div>
|
||||
|
||||
<a href="https://github.com/mpdavis/python-jose" class="external-link" target="_blank">Python-jose</a> requires a cryptographic backend as an extra.
|
||||
|
||||
Here we are using the recommended one: <a href="http://cryptography.io/" class="external-link" target="_blank">pyca/cryptography</a>.
|
||||
|
||||
!!! tip
|
||||
This tutorial previously used <a href="https://pyjwt.readthedocs.io/" class="external-link" target="_blank">PyJWT</a>.
|
||||
|
||||
But it was updated to use Python-jose instead as it provides all the features from PyJWT plus some extras that you might need later when building integrations with other tools.
|
||||
|
||||
## Password hashing
|
||||
|
||||
"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish.
|
||||
@@ -88,7 +97,7 @@ Import the tools we need from `passlib`.
|
||||
Create a PassLib "context". This is what will be used to hash and verify passwords.
|
||||
|
||||
!!! tip
|
||||
The PassLib context also has functionality to use different hashing algorithms, including deprecate old ones only to allow verifying them, etc.
|
||||
The PassLib context also has functionality to use different hashing algorithms, including deprecated old ones only to allow verifying them, etc.
|
||||
|
||||
For example, you could use it to read and verify passwords generated by another system (like Django) but hash any new passwords with a different algorithm like Bcrypt.
|
||||
|
||||
@@ -100,7 +109,7 @@ And another utility to verify if a received password matches the hash stored.
|
||||
|
||||
And another one to authenticate and return a user.
|
||||
|
||||
```Python hl_lines="7 48 55 56 59 60 69 70 71 72 73 74 75"
|
||||
```Python hl_lines="7 48 55-56 59-60 69-75"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -135,7 +144,7 @@ Define a Pydantic Model that will be used in the token endpoint for the response
|
||||
|
||||
Create a utility function to generate a new access token.
|
||||
|
||||
```Python hl_lines="3 6 12 13 14 28 29 30 78 79 80 81 82 83 84 85 86"
|
||||
```Python hl_lines="6 12-14 28-30 78-86"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -147,7 +156,7 @@ Decode the received token, verify it, and return the current user.
|
||||
|
||||
If the token is invalid, return an HTTP error right away.
|
||||
|
||||
```Python hl_lines="89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106"
|
||||
```Python hl_lines="89-106"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -157,7 +166,7 @@ Create a `timedelta` with the expiration time of the token.
|
||||
|
||||
Create a real JWT access token and return it.
|
||||
|
||||
```Python hl_lines="115 116 117 118 119 120 121 122 123 124 125 126 127 128"
|
||||
```Python hl_lines="115-128"
|
||||
{!../../../docs_src/security/tutorial004.py!}
|
||||
```
|
||||
|
||||
@@ -167,13 +176,13 @@ The JWT specification says that there's a key `sub`, with the subject of the tok
|
||||
|
||||
It's optional to use it, but that's where you would put the user's identification, so we are using it here.
|
||||
|
||||
JWT might be used for other things apart from identifying a user and allowing him to perform operations directly on your API.
|
||||
JWT might be used for other things apart from identifying a user and allowing them to perform operations directly on your API.
|
||||
|
||||
For example, you could identify a "car" or a "blog post".
|
||||
|
||||
Then you could add permissions about that entity, like "drive" (for the car) or "edit" (for the blog).
|
||||
|
||||
And then, you could give that JWT token to a user (or bot), and he could use it to perform those actions (drive the car, or edit the blog post) without even needing to have an account, just with the JWT token your API generated for that.
|
||||
And then, you could give that JWT token to a user (or bot), and they could use it to perform those actions (drive the car, or edit the blog post) without even needing to have an account, just with the JWT token your API generated for that.
|
||||
|
||||
Using these ideas, JWT can be used for way more sophisticated scenarios.
|
||||
|
||||
@@ -247,7 +256,7 @@ Many packages that simplify it a lot have to make many compromises with the data
|
||||
|
||||
It gives you all the flexibility to choose the ones that fit your project the best.
|
||||
|
||||
And you can use directly many well maintained and widely used packages like `passlib` and `pyjwt`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages.
|
||||
And you can use directly many well maintained and widely used packages like `passlib` and `python-jose`, because **FastAPI** doesn't require any complex mechanisms to integrate external packages.
|
||||
|
||||
But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness, or security.
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ They are normally used to declare specific security permissions, for example:
|
||||
In OAuth2 a "scope" is just a string that declares a specific permission required.
|
||||
|
||||
It doesn't matter if it has other characters like `:` or if it is a URL.
|
||||
|
||||
|
||||
Those details are implementation specific.
|
||||
|
||||
For OAuth2 they are just strings.
|
||||
@@ -47,9 +47,9 @@ Now let's use the utilities provided by **FastAPI** to handle this.
|
||||
|
||||
### `OAuth2PasswordRequestForm`
|
||||
|
||||
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` for the path `/token`:
|
||||
First, import `OAuth2PasswordRequestForm`, and use it as a dependency with `Depends` in the *path operation* for `/token`:
|
||||
|
||||
```Python hl_lines="2 74"
|
||||
```Python hl_lines="4 76"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -90,7 +90,7 @@ If there is no such user, we return an error saying "incorrect username or passw
|
||||
|
||||
For the error, we use the exception `HTTPException`:
|
||||
|
||||
```Python hl_lines="1 75 76 77"
|
||||
```Python hl_lines="3 77-79"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -118,7 +118,7 @@ If your database is stolen, the thief won't have your users' plaintext passwords
|
||||
|
||||
So, the thief won't be able to try to use those same passwords in another system (as many users use the same password everywhere, this would be dangerous).
|
||||
|
||||
```Python hl_lines="78 79 80 81"
|
||||
```Python hl_lines="80-83"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -156,7 +156,7 @@ For this simple example, we are going to just be completely insecure and return
|
||||
|
||||
But for now, let's focus on the specific details we need.
|
||||
|
||||
```Python hl_lines="83"
|
||||
```Python hl_lines="85"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
@@ -166,7 +166,7 @@ For this simple example, we are going to just be completely insecure and return
|
||||
This is something that you have to do yourself in your code, and make sure you use those JSON keys.
|
||||
|
||||
It's almost the only thing that you have to remember to do correctly yourself, to be compliant with the specifications.
|
||||
|
||||
|
||||
For the rest, **FastAPI** handles it for you.
|
||||
|
||||
## Update the dependencies
|
||||
@@ -177,11 +177,11 @@ We want to get the `current_user` *only* if this user is active.
|
||||
|
||||
So, we create an additional dependency `get_current_active_user` that in turn uses `get_current_user` as a dependency.
|
||||
|
||||
Both of these dependencies will just return an HTTP error if the user doesn't exists, or if is inactive.
|
||||
Both of these dependencies will just return an HTTP error if the user doesn't exist, or if is inactive.
|
||||
|
||||
So, in our endpoint, we will only get a user if the user exists, was correctly authenticated, and is active:
|
||||
|
||||
```Python hl_lines="56 57 58 59 60 61 62 63 64 65 67 68 69 70 88"
|
||||
```Python hl_lines="58-67 69-72 90"
|
||||
{!../../../docs_src/security/tutorial003.py!}
|
||||
```
|
||||
|
||||
|
||||