Compare commits

...

131 Commits

Author SHA1 Message Date
Sebastián Ramírez
3986f79029 🔖 Release version 0.25.0 2019-05-27 20:21:03 +04:00
Sebastián Ramírez
7379fde5ee 📝 Update release notes 2019-05-27 16:28:24 +04:00
Sebastián Ramírez
7b63bc5551 Add include, exclude, and by_alias to path operation methods (#264)
*  Make jsonable_encoder's include and exclude receive sequences

*  Add include, exclude, and by_alias to app and router

*  Add and update tutorial code with new parameters

* 📝 Update docs for new parameters and add docs for updating data

*  Add tests for consistency in path operation methods

*  Add tests for new parameters and update tests
2019-05-27 16:08:13 +04:00
Sebastián Ramírez
747ae8210f 📝 Update release notes 2019-05-25 19:48:27 +04:00
William Hayes
c651416e05 📝 Create CONTRIBUTING.md (#255) 2019-05-25 19:47:49 +04:00
Sebastián Ramírez
814f95e2bf 📝 Update release notes 2019-05-25 19:40:04 +04:00
William Hayes
d8716f94ae Add skip_defaults support for path operations (for #242) (#248) 2019-05-25 19:35:57 +04:00
Sebastián Ramírez
67f8cb3b4f 🔖 Release 0.24.0 2019-05-24 22:48:27 +04:00
Sebastián Ramírez
5c2828bd13 📝 Update release notes 2019-05-24 20:46:36 +04:00
James Kaplan
b087246f26 Add support for WebSockets with dependencies, params, etc #166 (#178) 2019-05-24 20:41:41 +04:00
Sebastián Ramírez
219d299426 📝 Update release notes 2019-05-23 11:04:13 +04:00
euri10
31da760729 ⬆️ Upgrade Pydantic to 0.26 (#247) 2019-05-23 11:03:53 +04:00
Sebastián Ramírez
fc89eb8f81 🔖 Release version 0.23.0 2019-05-21 23:26:28 +04:00
Sebastián Ramírez
6fca1041e9 📝 Update release notes 2019-05-21 23:05:46 +04:00
Sebastián Ramírez
9db1f5641b ⬆️ Upgrade Starlette to 0.12.0 (#243) 2019-05-21 22:59:48 +04:00
Sebastián Ramírez
c3beb56e63 📝 Update release notes 2019-05-21 22:46:22 +04:00
Steinthor Palsson
325edd5f00 Add swagger UI OAuth2 redirect page for implicit/code auth flows in API docs (#198) 2019-05-21 22:39:58 +04:00
Sebastián Ramírez
08322ef359 📝 Update release notes 2019-05-20 18:37:39 +04:00
Trim21
01b43e6e25 Make Swagger UI, ReDoc and OpenAPI handlers be coroutines to improve performance (#241) 2019-05-20 18:34:33 +04:00
Sebastián Ramírez
3cf92a156c 📝 Update release notes 2019-05-20 11:27:33 +04:00
euri10
f54d8d57a4 Make Swagger UI and ReDoc parameterizable to host offline assets for docs (#112) 2019-05-20 11:26:54 +04:00
Sebastián Ramírez
56ab106bbb 🔖 Release version 0.22.0 2019-05-16 18:09:11 +04:00
Sebastián Ramírez
e92b43b5c8 Add parameter dependencies to path operation decorators and include_router (#235)
*  Implement dependencies in decorator and .include_router

* 📝 Add docs for parameter dependencies

*  Add tests for dependencies parameter

* 🔥 Remove debugging prints in tests

* 📝 Update release notes
2019-05-16 18:07:00 +04:00
Sebastián Ramírez
7c50025c47 📝 Update release notes 2019-05-16 17:58:22 +04:00
euri10
adfbd27100 🐛 Fix OpenAPI URL format for Starlette convertors (#234) 2019-05-16 17:55:14 +04:00
Sebastián Ramírez
eada8bf7db 📝 Update release notes 2019-05-15 23:01:19 +04:00
Sebastián Ramírez
440b7a8efa 📝 Update relase notes 2019-05-15 22:14:25 +04:00
Sebastián Ramírez
fcaff64646 🔧 Separate format and lint scripts (#232) 2019-05-15 22:13:06 +04:00
Sebastián Ramírez
d240421378 📝 Add docs about params as functions for mypy (#231) 2019-05-15 22:01:23 +04:00
Sebastián Ramírez
ca27317b65 Add param functions, to override types, to make mypy happy (#226) 2019-05-15 21:25:11 +04:00
Sebastián Ramírez
ce02d3cb83 📝 Update release notes 2019-05-15 18:33:43 +04:00
Sebastián Ramírez
95475aaa9c 🔥 Remove Python version extraction in tests, no longer used 2019-05-15 18:33:13 +04:00
zamiramir
7a8b054a12 🎨 Reenable Black --check for Python 3.7 (#229)
Reenabled Black --check for python 3.7, issue is fixed.
see https://github.com/ambv/black/issues/494
2019-05-15 18:29:36 +04:00
Sebastián Ramírez
7b2993682f 🔖 Release 0.21.0 2019-05-15 14:54:46 +04:00
Sebastián Ramírez
73fad03b46 📝 Update release notes 2019-05-15 14:53:43 +04:00
Ricardo Momm
b0b88f9d5b 🔊 Raise from previous exception (#195) 2019-05-15 14:50:58 +04:00
Sebastián Ramírez
49d33f9f70 📝 Update release notes 2019-05-15 14:45:04 +04:00
Sebastián Ramírez
1f27981045 📝 Update release notes 2019-05-15 14:44:00 +04:00
euri10
f541d2c200 Use a logger instead of the root logging (#222) 2019-05-15 14:43:31 +04:00
euri10
0e99b23ebc ⬆️ Upgrade Pydantic to version 0.25 (#225) 2019-05-15 14:19:51 +04:00
Sebastián Ramírez
de341abe66 📝 Update release notes 2019-05-14 22:06:55 +04:00
Derek J. Lambert
4a1648b04e ✏️ Minor spelling fix in routing (#221) 2019-05-14 22:04:18 +04:00
Sebastián Ramírez
5f13b53ea5 🔧 Enable FastAPI releases bot in main Gitter channel 2019-05-11 19:44:39 +04:00
Sebastián Ramírez
5189c93d85 🔖 Release 0.20.1 2019-05-11 19:38:08 +04:00
Sebastián Ramírez
e612989313 📝 Update release notes 2019-05-11 13:46:17 +04:00
Steve B
d675991a34 add py.typed to ship typing information (#209)
As described in https://www.python.org/dev/peps/pep-0561/
2019-05-11 13:43:47 +04:00
Sebastián Ramírez
d03678dfbb 📝 Update release notes 2019-04-27 20:03:23 +04:00
Sebastián Ramírez
6ff89284c5 Add FastAPI releases bot for Gitter (#189)
* 🔥 Remove development util script

* 🎨 Reformat release notes with markdown-only code (no HTML)

*  Add FastAPI releases bot for Gitter
2019-04-27 20:02:32 +04:00
Sebastián Ramírez
7f673cf4e9 🔖 Release 0.20.0 2019-04-27 17:51:31 +04:00
Sebastián Ramírez
bac7230027 📝 Update release notes 2019-04-27 17:07:17 +04:00
Christopher Dignam
866af5bca6 ✏️ Fix typos in docs, from forms (#176) 2019-04-27 17:05:06 +04:00
Sebastián Ramírez
f4be79be51 📝 Update release notes 2019-04-27 17:04:00 +04:00
Sebastián Ramírez
3797c04946 Use 401 with WWW-Authenticate for OAuth2 and add scope_str (#188) 2019-04-27 17:00:56 +04:00
Sebastián Ramírez
9a9bfd7f93 📝 Update release notes 2019-04-26 19:28:54 +04:00
Sebastián Ramírez
ada1ecdb00 📝 Add Hypercorn as an alternative ASGI server (#187) 2019-04-26 19:26:38 +04:00
Sebastián Ramírez
3bbd38313b 📝 Update release notes 2019-04-26 19:08:04 +04:00
Sebastián Ramírez
c1df0f6b84 Add docs and tests for Jinja2 templates (#186)
*  Add docs and tests for Jinja2 templates

* 🎨 Fix format in test, remove unused import
2019-04-26 18:49:15 +04:00
Sebastián Ramírez
0cd5485597 📝 Update release notes 2019-04-26 15:17:32 +04:00
Sebastián Ramírez
528ef7e079 Docs and tests, responses with headers and cookies (#185) 2019-04-26 15:13:59 +04:00
Sebastián Ramírez
8e3a7699a3 🔖 Release 0.19.0 2019-04-26 13:48:30 +04:00
Sebastián Ramírez
8998ccaffb 📝 Update release notes 2019-04-26 13:45:38 +04:00
Sebastián Ramírez
2b7f201a44 📝 Add docs about returning a response directly and encoder (#184) 2019-04-26 13:40:23 +04:00
Sebastián Ramírez
192ebba2a2 ♻️ Rename parameter content_type to response_class (#183) 2019-04-26 13:11:16 +04:00
Sebastián Ramírez
8880c4cb03 🔖 Release 0.18.0 2019-04-22 21:08:43 +04:00
Sebastián Ramírez
6324be684f 📝 Update release notes 2019-04-21 22:31:43 +04:00
Sebastián Ramírez
c705685394 Add docs for HTTP Basic Auth and tests (#177) 2019-04-21 22:30:58 +04:00
Sebastián Ramírez
945f401d8e 📝 Update release notes 2019-04-21 21:46:00 +04:00
Sebastián Ramírez
f216d340ec Add automatic header handling for HTTP Basic Auth (#175)
*  Add automatic header handling for HTTP Basic Auth

* 🎨 Remove obsolete comment
2019-04-21 21:44:25 +04:00
Sebastián Ramírez
a4558e7053 📝 Update release notes 2019-04-21 20:21:53 +04:00
Sebastián Ramírez
298f8478e2 🔒 Fix development dependencies security (#174) 2019-04-21 20:20:25 +04:00
Sebastián Ramírez
b86d163470 📝 Rename additional response OpenAPI declarations 2019-04-21 20:13:26 +04:00
Sebastián Ramírez
9e2d37b89c 📝 Update release notes 2019-04-21 19:57:07 +04:00
Sebastián Ramírez
97adadd9e1 📝 Add docs for middleware (#173) 2019-04-21 19:56:20 +04:00
Sebastián Ramírez
26e3dffb37 🚀 Deploy when tagged using Python 3.6 2019-04-20 22:16:07 +04:00
Sebastián Ramírez
aa7b4bd101 🔖 0.17.0 2019-04-20 22:12:55 +04:00
Sebastián Ramírez
ffc4c716c0 🚀 Make Flit publish from CI (#170) 2019-04-20 22:09:35 +04:00
Sebastián Ramírez
ef7b6e8eaf 📝 Update Release Notes 2019-04-20 21:15:03 +04:00
Sebastián Ramírez
596243f4a5 Add docs about CORS (#169) 2019-04-20 21:13:01 +04:00
Sebastián Ramírez
766bf1c5aa 📝 Update release notes 2019-04-20 20:31:44 +04:00
Sebastián Ramírez
9e748dbca4 By default, encode by alias (#168) 2019-04-20 20:29:54 +04:00
Sebastián Ramírez
cefe6cf92c 🔖 Release version 0.16.0 2019-04-16 23:28:13 +04:00
Sebastián Ramírez
be3953499f 📝 Update release notes 2019-04-16 23:27:25 +04:00
Sebastián Ramírez
546d233dec ♻️ Update Pydantic usage, types, values, minor structure changes (#164) 2019-04-16 23:26:09 +04:00
Sebastián Ramírez
61dd36a945 Upgrade docstring Markdown parsing (#163)
*  Upgrade docstring Markdown parsing

* 📝 Update release notes
2019-04-16 22:49:18 +04:00
Sebastián Ramírez
27f9d55c3e 📝 Update release notes 2019-04-16 22:43:59 +04:00
euri10
906cc60f65 ⬆️ Upgrade Pydantic to 0.23 (#160)
* Add websocket to APIRouter

* Upgrade pydantic to v0.23.0

* Forgot pyproject.toml

* ⬆️ Upgrade some Pipfile.lock dependencies
2019-04-16 22:42:00 +04:00
Sebastián Ramírez
69afaf256f 📝 Update release notes 2019-04-16 22:21:32 +04:00
Daniel Michaels
4ab349a2a8 ✏️ fixed small typo /tutorial/extra-models.md (#159) 2019-04-16 22:20:03 +04:00
Sebastián Ramírez
9c258107b4 📝 Update release notes 2019-04-16 22:18:42 +04:00
hayata-yamamoto
29a4f90bcd 📝 fix URL examples in Tutorial: Query Parameters (#157)
* modify tutorial

* modify item_id
2019-04-16 22:16:16 +04:00
Sebastián Ramírez
361fd00777 📝 Add note about Swagger UI and multi-part uploads 2019-04-14 22:24:31 +04:00
Sebastián Ramírez
4c3cf31730 🔖 Release 0.15.0, multi-file uploads 2019-04-14 22:14:20 +04:00
Sebastián Ramírez
aad6b123f7 Add support for multi-file uploads (#158) 2019-04-14 22:12:14 +04:00
Sebastián Ramírez
e40e87c662 📝 Use same link in benchmarks as in index 2019-04-12 22:56:09 +04:00
Sebastián Ramírez
84de980977 Add docs about responses with additional status codes (#156)
*  Add docs about responses with additional status codes

* 📝 Update docs, link to documenting additional responses
2019-04-12 22:43:21 +04:00
Sebastián Ramírez
08484603ee 🔖 Release 0.14.0 2019-04-12 21:56:22 +04:00
Sebastián Ramírez
ab6dd60997 📝 Add note on installing and running pytest 2019-04-12 21:45:19 +04:00
Sebastián Ramírez
c9ef7bd6dc 📝 Update release notes 2019-04-12 21:27:24 +04:00
Sebastián Ramírez
88ece95a30 🎨 Improve automatic naming of path operations in API docs (#155)
* 🎨 Improve operation summary naming

* 📝 Update names in README

* 🚚 Improve names of security tutorial
2019-04-12 21:25:26 +04:00
Sebastián Ramírez
366c5db0bb 📝 Update release notes 2019-04-12 20:18:48 +04:00
Sebastián Ramírez
46e3811f8d Add testing docs and tests (#151)
* ✏️ Fix typo in security intro

*  Add testing docs and tests

* 🐛 Debug Travis coverage

* 🐛 Debug Travis coverage, report XML

* 💚 Make Travis/Flit use same code install

*  Revert Travis/Codecov debugging changes
2019-04-12 20:15:05 +04:00
Sebastián Ramírez
613e211d20 📝 Update release notes 2019-04-12 11:51:26 +04:00
William Hayes
500f2b2ad4 Add deeplinking to Swagger UI (#148)
Update the Swagger UI docs to deep link to path operations to share them more easily.
2019-04-12 11:49:32 +04:00
Sebastián Ramírez
5cf7718657 📝 Update release notes 2019-04-12 11:40:56 +04:00
Sebastián Ramírez
727b656c8d ⬆️ Update development dependencies, Pipfile.lock (#150) 2019-04-12 11:39:06 +04:00
Sebastián Ramírez
cc7102e9b8 📝 Add opinions in main page 2019-04-10 22:27:28 +04:00
Sebastián Ramírez
5123915fe4 📝 Include Hug and Falcon in Alternatives/Inspiration 2019-04-10 22:09:50 +04:00
Sebastián Ramírez
1b8bbd51d8 🔖 Release 0.13.0 2019-04-09 23:38:11 +04:00
Sebastián Ramírez
1e4f86db6d 📝 Update release notes and OAuth2 scopes docs 2019-04-09 23:36:18 +04:00
Sebastián Ramírez
7391056daf Add OAuth2 scopes with SecurityScopes, upgrade Security (#141)
*  Upgrade OAuth2 Security with scopes handling

* 📝 Update Security tutorial with OAuth2 and JWT

*  Add tutorial code for OAuth2 with scopes (and JWT)

*  Add tests for tutorial/OAuth2 with scopes

* 🐛 Fix security_scopes type declaration

*  Add docs and tests for SecurityScopes
2019-04-09 23:29:04 +04:00
Sebastián Ramírez
6a274d18b4 🔖 Release 0.12.1, fix responses in include_router 2019-04-05 20:08:36 +04:00
Sebastián Ramírez
62626b0175 📝 Update release notes 2019-04-05 20:08:00 +04:00
Sebastián Ramírez
c8df3ae57c 🐛 Fix handling additional responses in include_router (#140) 2019-04-05 20:06:40 +04:00
Sebastián Ramírez
6f7f9268f6 📝 Update release notes 2019-04-05 16:24:04 +04:00
Matthew McLeod
50653e205f 📝 Fix typo in SQL tutorial (#138) 2019-04-05 16:22:33 +04:00
Sebastián Ramírez
50a280b17b 📝 Update release notes 2019-04-05 16:21:17 +04:00
Mostapha Sadeghipour Roudsari
c1da3b38a3 📝 fix typos in nested models and OAuth2 with JWT (#127) 2019-04-05 16:08:59 +04:00
Sebastián Ramírez
e68a68c97c 🔖 Release 0.12.0, add additional responses 2019-04-05 14:35:01 +04:00
Sebastián Ramírez
907e613ff2 🔧 Update test-conv-html.sh to allow extra params 2019-04-05 14:29:36 +04:00
Sebastián Ramírez
f0fc2fad2c 📝 Update release notes 2019-04-05 14:28:30 +04:00
Sebastián Ramírez
ad471307e2 Additional Responses (#97)
Add additional responses to OpenAPI, including Pydantic models or schemas directly, custom status codes, media types, extending `response_model`, etc.
2019-04-05 14:18:28 +04:00
Sebastián Ramírez
2bd775988f Add/refactor addditional responses, tests, docs 2019-04-05 13:54:00 +04:00
Mohammed
eda9b28338 files formatting 2019-03-23 13:10:45 +03:00
Mohammed
7514ac6fb0 100% test coverage 2019-03-23 13:01:53 +03:00
Mohammed
25fb4239cc increase test coverage 2019-03-23 01:13:09 +03:00
Mohammed
65568065e0 Remove extra code. 2019-03-23 00:47:32 +03:00
Mohammed
95679ca5e6 Fix: adding additional_responses on .include_router() 2019-03-23 00:37:10 +03:00
Mohammed
84a300ef84 Formatting according to guide 2019-03-22 22:54:48 +03:00
Mohammed
c6d28c8209 Accept Multiple Additional Responses 2019-03-22 22:50:47 +03:00
Mohammed
3984e9b8ac Additional Responses test 2019-03-22 22:40:46 +03:00
Mohammed
aa0bca7bb2 Additional Responses implementation 2019-03-22 22:40:07 +03:00
233 changed files with 7089 additions and 912 deletions

View File

@@ -10,7 +10,7 @@ python:
install:
- pip install flit
- flit install
- flit install --symlink
script:
- bash scripts/test.sh
@@ -20,6 +20,7 @@ after_script:
deploy:
provider: script
script: bash scripts/trigger-docker.sh
script: bash scripts/deploy.sh
on:
branch: master
tags: true
python: "3.6"

1
CONTRIBUTING.md Normal file
View File

@@ -0,0 +1 @@
Please read the [Development - Contributing](https://fastapi.tiangolo.com/contributing/) guidelines in the documentation site.

View File

@@ -25,9 +25,10 @@ sqlalchemy = "*"
uvicorn = "*"
[packages]
starlette = "==0.11.1"
pydantic = "==0.21.0"
starlette = "==0.12.0"
pydantic = "==0.26.0"
databases = {extras = ["sqlite"],version = "*"}
hypercorn = "*"
[requires]
python_version = "3.6"

380
Pipfile.lock generated
View File

@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
"sha256": "24b3b7b88d3cbe671ddbe296e64c15f8558f0e5d5df977200119872a363aac13"
"sha256": "4a33b47e814fa75533548874ffadbc6163b3058db4d1615ff633512366d72ccb"
},
"pipfile-spec": 6,
"requires": {
@@ -18,34 +18,33 @@
"default": {
"aiocontextvars": {
"hashes": [
"sha256:1e0ff5837c8b01c36a1107acdd0baf7853ebdf6c9fc43e8e311f4be37ac2038a",
"sha256:6ff7aee14f549d52f0446cbb84d0deddcd3fc677bcf8fbc2ce13f5756d2064dc"
"sha256:885daf8261818767d8f7cbd79f9d4482d118f024b6586ef6e67980236a27bfa3",
"sha256:f027372dc48641f683c559f247bd84962becaacdc9ba711d583c3871fb5652aa"
],
"markers": "python_version < '3.7'",
"version": "==0.2.1"
"version": "==0.2.2"
},
"aiosqlite": {
"hashes": [
"sha256:af4fed9e778756fa0ffffc7a8b14c4d7b1a57155dc5669f18e45107313f6019e"
"sha256:ad84fbd7516ca7065d799504fc41d6845c938e5306d1b7dd960caaeda12e22a9"
],
"version": "==0.9.0"
"version": "==0.10.0"
},
"contextvars": {
"hashes": [
"sha256:2341042e1c03a271813e07dba29b6b60fa85c1005ea5ed1638a076cf50b4d625"
"sha256:f38c908aaa59c14335eeea12abea5f443646216c4e29380d7bf34d2018e2c39e"
],
"markers": "python_version < '3.7'",
"version": "==2.3"
"version": "==2.4"
},
"databases": {
"extras": [
"sqlite"
],
"hashes": [
"sha256:4a0f15669c390a04b439972426350c0ae921ddc08c42bd54f125eb2fb86ee728"
"sha256:d365cff2035c5177ef5fd8c5abf6671da01189521da64848a01251c870daf48f"
],
"index": "pypi",
"version": "==0.2.0"
"version": "==0.2.2"
},
"dataclasses": {
"hashes": [
@@ -55,42 +54,104 @@
"markers": "python_version < '3.7'",
"version": "==0.6"
},
"h11": {
"hashes": [
"sha256:acca6a44cb52a32ab442b1779adf0875c443c689e9e028f8d831a3769f9c5208",
"sha256:f2b1ca39bfed357d1f19ac732913d5f9faa54a5062eca7d2ec3a916cfb7ae4c7"
],
"version": "==0.8.1"
},
"h2": {
"hashes": [
"sha256:c8f387e0e4878904d4978cd688a3195f6b169d49b1ffa572a3d347d7adc5e09f",
"sha256:fd07e865a3272ac6ef195d8904de92dc7b38dc28297ec39cfa22716b6d62e6eb"
],
"version": "==3.1.0"
},
"hpack": {
"hashes": [
"sha256:0edd79eda27a53ba5be2dfabf3b15780928a0dff6eb0c60a3d6767720e970c89",
"sha256:8eec9c1f4bfae3408a3f30500261f7e6a65912dc138526ea054f9ad98892e9d2"
],
"version": "==3.0.0"
},
"hypercorn": {
"hashes": [
"sha256:cfe7811a93ab7bc22c8a0d5514a2a7a512e812c1e4ee13b9731b705b79d4d453",
"sha256:f2577806223fa44d57d6f136b6c37a046794f961252699aec8afb15f35d226d5"
],
"index": "pypi",
"version": "==0.5.4"
},
"hyperframe": {
"hashes": [
"sha256:5187962cb16dcc078f23cb5a4b110098d546c3f41ff2d4038a9896893bbd0b40",
"sha256:a9f5c17f2cc3c719b917c4f33ed1c61bd1f8dfac4b1bd23b7c80b3400971b41f"
],
"version": "==5.2.0"
},
"immutables": {
"hashes": [
"sha256:1e4f4513254ef11e0230a558ee0dcb4551b914993c330005d15338da595d3750",
"sha256:228e38dc7a810ba4ff88909908ac47f840e5dc6c4c0da6b25009c626a9ae771c",
"sha256:2ae88fbfe1d04f4e5859c924e97313edf70e72b4f19871bf329b96a67ede9ba0",
"sha256:2d32b61c222cba1dd11f0faff67c7fb6204ef1982454e1b5b001d4b79966ef17",
"sha256:35af186bfac5b62522fdf2cab11120d7b0547f405aa399b6a1e443cf5f5e318c",
"sha256:63023fa0cceedc62e0d1535cd4ca7a1f6df3120a6d8e5c34e89037402a6fd809",
"sha256:6bf5857f42a96331fd0929c357dc0b36a72f339f3b6acaf870b149c96b141f69",
"sha256:7bb1590024a032c7a57f79faf8c8ff5e91340662550d2980e0177f67e66e9c9c",
"sha256:7c090687d7e623d4eca22962635b5e1a1ee2d6f9a9aca2f3fb5a184a1ffef1f2",
"sha256:bc36a0a8749881eebd753f696b081bd51145e4d77291d671d2e2f622e5b65d2f",
"sha256:d9fc6a236018d99af6453ead945a6bb55f98d14b1801a2c229dd993edc753a00"
"sha256:10861f2a2b86139f0c91d5073392d76117f37e84f912dc47c943c23a64008cc7",
"sha256:3e23eeb4bc55d57b2a97bef4c1a2891bbb731050b4167c855545797d45e84e45",
"sha256:4373876879f147986808f71e6ca02380192a279e8b8d45832f6fed4e7f717562",
"sha256:46f9122da033fecf84d7f4c6257aec780f370b20f3ce6bc521702b63ee3d99f7",
"sha256:5104db6102e53702af45c6b0af36e45a80970123b11a80c14e0fce48444cdbe3",
"sha256:59274bcb631f4fdc9731e9a4a96d16d96b3a17e29fd5e46516518f38406f678f",
"sha256:65a9c624e50ca5c50464dbf432996b5c4f056a411bcff5690ef4cab59f913f99",
"sha256:b64e0672497b884d21170ca61c693da8488d77f043650efa7911378cbbad0f2c",
"sha256:b70655dba00742b033310933066a2202e1cfbbb0f63841b4597cd8787974b242",
"sha256:c3d8c238a6f9b60355578579563773348674b6da63c1a0d7394384ed341f3d41",
"sha256:cd66bcd11b6a1c1a80fb8d90e25870ff2d5c705ab5eb9666355a33d3fef6ac70",
"sha256:d59310fc4f97c1ff8c3660cb98032db266ac0c285a86ca7a512e8e84a95f44c9",
"sha256:d71d1c822498646143270580dd6f743bb31ab89ae0ded8b2307c356d3a00f1c0",
"sha256:f53da698b42db83cfb1f5073560838051430798c8d8e34a57a27031edbc3041d",
"sha256:f958ba15745e30d3a38e3c9fcead8496037135bb21c78c0f925c104abba3a6fa",
"sha256:ff95e2aa618eed1a0ef4479938f18f3522c89562b9bbb59d677597c0337569dd"
],
"version": "==0.6"
"version": "==0.9"
},
"pydantic": {
"hashes": [
"sha256:93fa585402e7c8c01623ea8af6ca23363e8b4c6a020b7a2de9e99fa29d642d50",
"sha256:eb441dd50779347a450494c437db3ecbb13c1f3854497df879662782af516c5c"
"sha256:b72e0df2463cee746cf42639845d4106c19f30136375e779352d710e69617731",
"sha256:dab99d3070e040b8b2e987dfbe237350ab92d5d57a22d4e0e268ede2d85c7964"
],
"index": "pypi",
"version": "==0.21.0"
"version": "==0.26.0"
},
"pytoml": {
"hashes": [
"sha256:ca2d0cb127c938b8b76a9a0d0f855cf930c1d50cc3a0af6d3595b566519a1013"
],
"version": "==0.1.20"
},
"sqlalchemy": {
"hashes": [
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
"sha256:91c54ca8345008fceaec987e10924bf07dcab36c442925357e5a467b36a38319"
],
"version": "==1.3.1"
"version": "==1.3.3"
},
"starlette": {
"hashes": [
"sha256:9d48b35d1fc7521d59ae53c421297ab3878d3c7cd4b75266d77f6c73cccb78bb"
"sha256:d313433ef5cc38e0a276b59688ca2b11b8f031c78808c1afdf9d55cb86f34590"
],
"index": "pypi",
"version": "==0.11.1"
"version": "==0.12.0"
},
"typing-extensions": {
"hashes": [
"sha256:07b2c978670896022a43c4b915df8958bec4a6b84add7f2c87b2b728bda3ba64",
"sha256:f3f0e67e1d42de47b5c67c32c9b26641642e9170fe7e292991793705cd5fef7c",
"sha256:fb2cd053238d33a8ec939190f30cfd736c00653a85a2919415cecf7dc3d9da71"
],
"version": "==3.7.2"
},
"wsproto": {
"hashes": [
"sha256:55c3da870460e8838b2fbe4d10f3accc0cea3a13d5e8dbbdc6da5d537d6d44dc",
"sha256:c7f35e0af250b9f25583b090039eb2159a079fbe71b7daf86cc3ddcd2f3a70b3"
],
"version": "==0.14.0"
}
},
"develop": {
@@ -117,10 +178,10 @@
},
"autoflake": {
"hashes": [
"sha256:c103e63466f11db3617167a2c68ff6a0cda35b940222920631c6eeec6b67e807"
"sha256:6b59e5b9b82e30077499578856282debb81186d10b4f899e8c2e1d616cdef973"
],
"index": "pypi",
"version": "==1.2"
"version": "==1.3"
},
"backcall": {
"hashes": [
@@ -175,37 +236,35 @@
},
"coverage": {
"hashes": [
"sha256:029c69deaeeeae1b15bc6c59f0ffa28aa8473721c614a23f2c2976dec245cd12",
"sha256:02abbbebc6e9d5abe13cd28b5e963dedb6ffb51c146c916d17b18f141acd9947",
"sha256:1bbfe5b82a3921d285e999c6d256c1e16b31c554c29da62d326f86c173d30337",
"sha256:210c02f923df33a8d0e461c86fdcbbb17228ff4f6d92609fc06370a98d283c2d",
"sha256:2d0807ba935f540d20b49d5bf1c0237b90ce81e133402feda906e540003f2f7a",
"sha256:35d7a013874a7c927ce997350d314144ffc5465faf787bb4e46e6c4f381ef562",
"sha256:3636f9d0dcb01aed4180ef2e57a4e34bb4cac3ecd203c2a23db8526d86ab2fb4",
"sha256:42f4be770af2455a75e4640f033a82c62f3fb0d7a074123266e143269d7010ef",
"sha256:48440b25ba6cda72d4c638f3a9efa827b5b87b489c96ab5f4ff597d976413156",
"sha256:4dac8dfd1acf6a3ac657475dfdc66c621f291b1b7422a939cc33c13ac5356473",
"sha256:4e8474771c69c2991d5eab65764289a7dd450bbea050bc0ebb42b678d8222b42",
"sha256:551f10ddfeff56a1325e5a34eff304c5892aa981fd810babb98bfee77ee2fb17",
"sha256:5b104982f1809c1577912519eb249f17d9d7e66304ad026666cb60a5ef73309c",
"sha256:5c62aef73dfc87bfcca32cee149a1a7a602bc74bac72223236b0023543511c88",
"sha256:633151f8d1ad9467b9f7e90854a7f46ed8f2919e8bc7d98d737833e8938fc081",
"sha256:772207b9e2d5bf3f9d283b88915723e4e92d9a62c83f44ec92b9bd0cd685541b",
"sha256:7d5e02f647cd727afc2659ec14d4d1cc0508c47e6cfb07aea33d7aa9ca94d288",
"sha256:a9798a4111abb0f94584000ba2a2c74841f2cfe5f9254709756367aabbae0541",
"sha256:b38ea741ab9e35bfa7015c93c93bbd6a1623428f97a67083fc8ebd366238b91f",
"sha256:b6a5478c904236543c0347db8a05fac6fc0bd574c870e7970faa88e1d9890044",
"sha256:c6248bfc1de36a3844685a2e10ba17c18119ba6252547f921062a323fb31bff1",
"sha256:c705ab445936457359b1424ef25ccc0098b0491b26064677c39f1d14a539f056",
"sha256:d95a363d663ceee647291131dbd213af258df24f41350246842481ec3709bd33",
"sha256:e27265eb80cdc5dab55a40ef6f890e04ecc618649ad3da5265f128b141f93f78",
"sha256:ebc276c9cb5d917bd2ae959f84ffc279acafa9c9b50b0fa436ebb70bbe2166ea",
"sha256:f4d229866d030863d0fe3bf297d6d11e6133ca15bbb41ed2534a8b9a3d6bd061",
"sha256:f95675bd88b51474d4fe5165f3266f419ce754ffadfb97f10323931fa9ac95e5",
"sha256:f95bc54fb6d61b9f9ff09c4ae8ff6a3f5edc937cda3ca36fc937302a7c152bf1",
"sha256:fd0f6be53de40683584e5331c341e65a679dbe5ec489a0697cec7c2ef1a48cda"
"sha256:0402b1822d513d0231589494bceddb067d20581f5083598c451b56c684b0e5d6",
"sha256:0644e28e8aea9d9d563607ee8b7071b07dd57a4a3de11f8684cd33c51c0d1b93",
"sha256:0874a283686803884ec0665018881130604956dbaa344f2539c46d82cbe29eda",
"sha256:0988c3837df4bc371189bb3425d5232cf150055452034c232dda9cbe04f9c38e",
"sha256:20bc3205b3100956bb72293fabb97f0ed972c81fed10b3251c90c70dcb0599ab",
"sha256:2cc9142a3367e74eb6b19d58c53ebb1dfd7336b91cdcc91a6a2888bf8c7af984",
"sha256:3ae9a0a59b058ce0761c3bd2c2d66ecb2ee2b8ac592620184370577f7a546fb3",
"sha256:3b2e30b835df58cb973f478d09f3d82e90c98c8e5059acc245a8e4607e023801",
"sha256:401e9b04894eb1498c639c6623ee78a646990ce5f095248e2440968aafd6e90e",
"sha256:41ec5812d5decdaa72708be3018e7443e90def4b5a71294236a4df192cf9eab9",
"sha256:475769b638a055e75b3d3219e054fe2a023c0b077ff15bff6c95aba7e93e6cac",
"sha256:61424f4e2e82c4129a4ba71e10ebacb32a9ecd6f80de2cd05bdead6ba75ed736",
"sha256:811969904d4dd0bee7d958898be8d9d75cef672d9b7e7db819dfeac3d20d2d0c",
"sha256:86224bb99abfd672bf2f9fcecad5e8d7a3fa94f7f71513f2210460a0350307cd",
"sha256:9a238a20a3af00665f8381f7e53e9c606f9bb652d2423f6b822f6cb790d887e8",
"sha256:a23b3fbc14d4e6182ecebfd22f3729beef0636d151d94764a1c28330d185e4e5",
"sha256:ac162b4ebe51b7a2b7f5e462c4402802633eb81e77c94f8a7c1ed8a556e72c75",
"sha256:b6187378726c84365bf297b5dcdae8789b6a5823b200bea23797777e5a63be09",
"sha256:bcd5723d905ed4a825f17410a53535f880b6d7548ae3d89078db7b1ceefcd853",
"sha256:c48a4f9c5fb385269bb7fbaf9c1326a94863b65ec7f5c96b2ea56b252f01ad08",
"sha256:cd40199d6f1c29c85b170d25589be9a97edff8ee7e62be180a2a137823896030",
"sha256:d1bc331a7d069485ac1d8c25a0ea1f6aab6cb2a87146fb652222481c1bddc9ff",
"sha256:d7e0cdc249aa0f94aa2e531b03999ddaf03a10b4fa090a894712d4c8066abd89",
"sha256:e9ee8fcd8e067fcc5d7276d46e07e863102b70a52545ef4254df1ff0893ce75f",
"sha256:eb313c23d983b7810504f42104e8dcd1c7ccdda8fbaab82aab92ab79fea19345",
"sha256:f9cfd478654b509941b85ed70f870f5e3c74678f566bec12fd26545e5340ba47",
"sha256:fae1fa144034d021a52cb9ea200eb8dedf91869c6df8202ad5d149b41ed91cc8"
],
"version": "==5.0a4"
"version": "==5.0a5"
},
"decorator": {
"hashes": [
@@ -216,10 +275,10 @@
},
"defusedxml": {
"hashes": [
"sha256:24d7f2f94f7f3cb6061acb215685e5125fbcdc40a857eff9de22518820b0a4f4",
"sha256:702a91ade2968a82beb0db1e0766a6a273f33d4616a6ce8cde475d8e09853b20"
"sha256:6687150770438374ab581bb7a1b327a847dd9c5749e396102de3fad4e8a3ef93",
"sha256:f684034d135af4c6cbb949b8a4d2ed61634515257a67299e5f940fbaa34377f5"
],
"version": "==0.5.0"
"version": "==0.6.0"
},
"dnspython": {
"hashes": [
@@ -238,10 +297,10 @@
},
"email-validator": {
"hashes": [
"sha256:ddc4b5b59fa699bb10127adcf7ad4de78fde4ec539a072b104b8bb16da666ae5"
"sha256:79966e318d6d68fed359c90f8f19d242bcc178b724011f1c07145bd093da6cc7"
],
"index": "pypi",
"version": "==1.0.3"
"version": "==1.0.4"
},
"entrypoints": {
"hashes": [
@@ -288,18 +347,18 @@
},
"ipykernel": {
"hashes": [
"sha256:0aeb7ec277ac42cc2b59ae3d08b10909b2ec161dc6908096210527162b53675d",
"sha256:0fc0bf97920d454102168ec2008620066878848fcfca06c22b669696212e292f"
"sha256:346189536b88859937b5f4848a6fd85d1ad0729f01724a411de5cae9b618819c",
"sha256:f0e962052718068ad3b1d8bcc703794660858f58803c3798628817f492a8769c"
],
"version": "==5.1.0"
"version": "==5.1.1"
},
"ipython": {
"hashes": [
"sha256:b038baa489c38f6d853a3cfc4c635b0cda66f2864d136fe8f40c1a6e334e2a6b",
"sha256:f5102c1cd67e399ec8ea66bcebe6e3968ea25a8977e53f012963e5affeb1fe38"
"sha256:54c5a8aa1eadd269ac210b96923688ccf01ebb2d0f21c18c3c717909583579a8",
"sha256:e840810029224b56cd0d9e7719dc3b39cf84d577f8ac686547c8ba7a06eeab26"
],
"markers": "python_version >= '3.3'",
"version": "==7.4.0"
"version": "==7.5.0"
},
"ipython-genutils": {
"hashes": [
@@ -317,11 +376,11 @@
},
"isort": {
"hashes": [
"sha256:08f8e3f0f0b7249e9fad7e5c41e2113aba44969798a26452ee790c06f155d4ec",
"sha256:4e9e9c4bd1acd66cf6c36973f29b031ec752cbfd991c69695e4e259f9a756927"
"sha256:c40744b6bc5162bbb39c1257fe298b7a393861d50978b565f3ccd9cb9de0182a",
"sha256:f57abacd059dc3bd666258d1efb0377510a89777fda3e3274e3c01f7c03ae22d"
],
"index": "pypi",
"version": "==4.3.16"
"version": "==4.3.20"
},
"jedi": {
"hashes": [
@@ -332,10 +391,10 @@
},
"jinja2": {
"hashes": [
"sha256:74c935a1b8bb9a3947c50a54766a969d4846290e1e788ea44c1392163723c3bd",
"sha256:f84be1bb0040caca4cea721fcbbbbd61f9be9464ca236387158b0feea01914a4"
"sha256:065c4f02ebe7f7cf559e49ee5a95fb800a9e4528727aec6f24402a5374c65013",
"sha256:14dd6caf1527abb21f08f86c784eac40853ba93edb79552aa1e4b8aef1b61c7b"
],
"version": "==2.10"
"version": "==2.10.1"
},
"jsonschema": {
"hashes": [
@@ -376,17 +435,17 @@
},
"livereload": {
"hashes": [
"sha256:29cadfabcedd12eed792e0131991235b9d4764d4474bed75cf525f57109ec0a2",
"sha256:e632a6cd1d349155c1d7f13a65be873b38f43ef02961804a1bba8d817fa649a7"
"sha256:78d55f2c268a8823ba499305dcac64e28ddeb9a92571e12d543cd304faf5817b",
"sha256:89254f78d7529d7ea0a3417d224c34287ebfe266b05e67e51facaf82c27f0f66"
],
"version": "==2.6.0"
"version": "==2.6.1"
},
"markdown": {
"hashes": [
"sha256:c00429bd503a47ec88d5e30a751e147dcb4c6889663cd3e2ba0afe858e009baa",
"sha256:d02e0f9b04c500cde6637c11ad7c72671f359b87b9fe924b2383649d8841db7c"
"sha256:2e50876bcdd74517e7b71f3e7a76102050edec255b3983403f1a63e7c8a41e7a",
"sha256:56a46ac655704b91e5b7e6326ce43d5ef72411376588afa1dd90e881b83c7e8c"
],
"version": "==3.0.1"
"version": "==3.1.1"
},
"markdown-include": {
"hashes": [
@@ -452,27 +511,36 @@
},
"mkdocs-material": {
"hashes": [
"sha256:0b394aa034b25a09a5874ae2a6ccc426fd81f5764e0991217b169e31cb0c1c0e",
"sha256:f5bb80a2c16d045d380edb2c5b05636af1bb709cb859bfaa9d01063a11df803f"
"sha256:1c39b6af13a900d9f47ab2b8ac67b3258799f4570b552573e9d6868ad6a438e9",
"sha256:22073941cff7176e810b719aced6a90381e64a96d346b8a6803a06b7192b7ad5"
],
"index": "pypi",
"version": "==4.1.0"
"version": "==4.3.0"
},
"more-itertools": {
"hashes": [
"sha256:0125e8f60e9e031347105eb1682cef932f5e97d7b9a1a28d9bf00c22a5daef40",
"sha256:590044e3942351a1bdb1de960b739ff4ce277960f2425ad4509446dbace8d9d1"
"sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
"sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
],
"markers": "python_version > '2.7'",
"version": "==6.0.0"
"version": "==7.0.0"
},
"mypy": {
"hashes": [
"sha256:308c274eb8482fbf16006f549137ddc0d69e5a589465e37b99c4564414363ca7",
"sha256:e80fd6af34614a0e898a57f14296d0dacb584648f0339c2e000ddbf0f4cc2f8d"
"sha256:2afe51527b1f6cdc4a5f34fc90473109b22bf7f21086ba3e9451857cf11489e6",
"sha256:56a16df3e0abb145d8accd5dbb70eba6c4bd26e2f89042b491faa78c9635d1e2",
"sha256:5764f10d27b2e93c84f70af5778941b8f4aa1379b2430f85c827e0f5464e8714",
"sha256:5bbc86374f04a3aa817622f98e40375ccb28c4836f36b66706cf3c6ccce86eda",
"sha256:6a9343089f6377e71e20ca734cd8e7ac25d36478a9df580efabfe9059819bf82",
"sha256:6c9851bc4a23dc1d854d3f5dfd5f20a016f8da86bcdbb42687879bb5f86434b0",
"sha256:b8e85956af3fcf043d6f87c91cbe8705073fc67029ba6e22d3468bfee42c4823",
"sha256:b9a0af8fae490306bc112229000aa0c2ccc837b49d29a5c42e088c132a2334dd",
"sha256:bbf643528e2a55df2c1587008d6e3bda5c0445f1240dfa85129af22ae16d7a9a",
"sha256:c46ab3438bd21511db0f2c612d89d8344154c0c9494afc7fbc932de514cf8d15",
"sha256:f7a83d6bd805855ef83ec605eb01ab4fa42bcef254b13631e451cbb44914a9b0"
],
"index": "pypi",
"version": "==0.670"
"version": "==0.701"
},
"mypy-extensions": {
"hashes": [
@@ -483,10 +551,10 @@
},
"nbconvert": {
"hashes": [
"sha256:302554a2e219bc0fc84f3edd3e79953f3767b46ab67626fdec16e38ba3f7efe4",
"sha256:5de8fb2284422272a1d45abc77c07b888127550a6d602ce619592a2b08a474ff"
"sha256:138381baa41d83584459b5cfecfc38c800ccf1f37d9ddd0bd440783346a4c39c",
"sha256:4a978548d8383f6b2cfca4a3b0543afb77bc7cb5a96e8b424337ab58c12da9bc"
],
"version": "==5.4.1"
"version": "==5.5.0"
},
"nbformat": {
"hashes": [
@@ -497,10 +565,10 @@
},
"notebook": {
"hashes": [
"sha256:18a98858c0331fb65a60f2ebb6439f8c0c4defd14ca363731b6cabc7f61624b4",
"sha256:cc027a62be0f7756e0ef3d2d98458c4d7f4b3566449fb1a05891207f5bd9a1bf"
"sha256:573e0ae650c5d76b18b6e564ba6d21bf321d00847de1d215b418acb64f056eb8",
"sha256:f64fa6624d2323fbef6210a621817d6505a45d0d4a9367f1843b20a38a4666ee"
],
"version": "==5.7.6"
"version": "==5.7.8"
},
"pandocfilters": {
"hashes": [
@@ -510,18 +578,18 @@
},
"parso": {
"hashes": [
"sha256:4580328ae3f548b358f4901e38c0578229186835f0fa0846e47369796dd5bcc9",
"sha256:68406ebd7eafe17f8e40e15a84b56848eccbf27d7c1feb89e93d8fca395706db"
"sha256:17cc2d7a945eb42c3569d4564cdf49bde221bc2b552af3eca9c1aad517dcdd33",
"sha256:2e9574cb12e7112a87253e14e2c380ce312060269d04bd018478a3c92ea9a376"
],
"version": "==0.3.4"
"version": "==0.4.0"
},
"pexpect": {
"hashes": [
"sha256:2a8e88259839571d1251d278476f3eec5db26deb73a70be5ed5dc5435e418aba",
"sha256:3fbd41d4caf27fa4a377bfd16fef87271099463e6fa73e92a52f92dfee5d425b"
"sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1",
"sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb"
],
"markers": "sys_platform != 'win32'",
"version": "==4.6.0"
"version": "==4.7.0"
},
"pickleshare": {
"hashes": [
@@ -532,10 +600,10 @@
},
"pluggy": {
"hashes": [
"sha256:19ecf9ce9db2fce065a7a0586e07cfb4ac8614fe96edf628a264b1c70116cf8f",
"sha256:84d306a647cc805219916e62aab89caa97a33a1dd8c342e87a37f91073cd4746"
"sha256:25a1bc1d148c9a640211872b4ff859878d422bccb59c9965e04eed468a0aa180",
"sha256:964cedd2b27c492fbf0b7f58b3284a09cf7f99b0f715941fb24a439b3af1bd1a"
],
"version": "==0.9.0"
"version": "==0.11.0"
},
"prometheus-client": {
"hashes": [
@@ -582,10 +650,10 @@
},
"pygments": {
"hashes": [
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
"sha256:31cba6ffb739f099a85e243eff8cb717089fdd3c7300767d9fc34cb8e1b065f5",
"sha256:5ad302949b3c98dd73f8d9fcdc7e9cb592f120e32a18e23efd7f3dc51194472b"
],
"version": "==2.3.1"
"version": "==2.4.0"
},
"pymdown-extensions": {
"hashes": [
@@ -596,25 +664,25 @@
},
"pyrsistent": {
"hashes": [
"sha256:3ca82748918eb65e2d89f222b702277099aca77e34843c5eb9d52451173970e2"
"sha256:16692ee739d42cf5e39cef8d27649a8c1fdb7aa99887098f1460057c5eb75c3a"
],
"version": "==0.14.11"
"version": "==0.15.2"
},
"pytest": {
"hashes": [
"sha256:592eaa2c33fae68c7d75aacf042efc9f77b27c08a6224a4f59beab8d9a420523",
"sha256:ad3ad5c450284819ecde191a654c09b0ec72257a2c711b9633d677c71c9850c4"
"sha256:1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24",
"sha256:b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6"
],
"index": "pypi",
"version": "==4.3.1"
"version": "==4.5.0"
},
"pytest-cov": {
"hashes": [
"sha256:0ab664b25c6aa9716cbf203b17ddb301932383046082c081b9848a0edf5add33",
"sha256:230ef817450ab0699c6cc3c9c8f7a829c34674456f2ed8df1fe1d39780f7c87f"
"sha256:2b097cde81a302e1047331b48cadacf23577e431b61e9c6f49a1170bbe3d3da6",
"sha256:e00ea4fdde970725482f1f35630d12f074e121a23801aabf2ae154ec6bdd343a"
],
"index": "pypi",
"version": "==2.6.1"
"version": "==2.7.1"
},
"python-dateutil": {
"hashes": [
@@ -684,18 +752,18 @@
},
"qtconsole": {
"hashes": [
"sha256:1ac4a65e81a27b0838330a6d351c2f8435d4013d98a95373e8a41119b2968390",
"sha256:bc1ba15f50c29ed50f1268ad823bb6543be263c18dd093b80495e9df63b003ac"
"sha256:a667558c7b1e1442a2e5bcef1686c55e096efd0b58d8b2a0a8415f4579991ee3",
"sha256:fdfc6002d9d2834c88f9c92e0f6f590284ff3740fa53016f188a62d58bcca6d8"
],
"version": "==4.4.3"
"version": "==4.4.4"
},
"requests": {
"hashes": [
"sha256:502a824f31acdacb3a35b6690b5fbf0bc41d63a24a45c4004352b0242707598e",
"sha256:7bf2a778576d825600030a110f3c0e3e8edc51dfaafe1c146e39a2027784957b"
"sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4",
"sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31"
],
"index": "pypi",
"version": "==2.21.0"
"version": "==2.22.0"
},
"send2trash": {
"hashes": [
@@ -713,16 +781,16 @@
},
"sqlalchemy": {
"hashes": [
"sha256:781fb7b9d194ed3fc596b8f0dd4623ff160e3e825dd8c15472376a438c19598b"
"sha256:91c54ca8345008fceaec987e10924bf07dcab36c442925357e5a467b36a38319"
],
"version": "==1.3.1"
"version": "==1.3.3"
},
"terminado": {
"hashes": [
"sha256:55abf9ade563b8f9be1f34e4233c7b7bde726059947a593322e8a553cc4c067a",
"sha256:65011551baff97f5414c67018e908110693143cfbaeb16831b743fe7cad8b927"
"sha256:d9d012de63acb8223ac969c17c3043337c2fcfd28f3aea1ee429b345d01ef460",
"sha256:de08e141f83c3a0798b050ecb097ab6259c3f0331b2f7b7750c9075ced2c20c2"
],
"version": "==0.8.1"
"version": "==0.8.2"
},
"testpath": {
"hashes": [
@@ -759,27 +827,27 @@
},
"typed-ast": {
"hashes": [
"sha256:035a54ede6ce1380599b2ce57844c6554666522e376bd111eb940fbc7c3dad23",
"sha256:037c35f2741ce3a9ac0d55abfcd119133cbd821fffa4461397718287092d9d15",
"sha256:049feae7e9f180b64efacbdc36b3af64a00393a47be22fa9cb6794e68d4e73d3",
"sha256:19228f7940beafc1ba21a6e8e070e0b0bfd1457902a3a81709762b8b9039b88d",
"sha256:2ea681e91e3550a30c2265d2916f40a5f5d89b59469a20f3bad7d07adee0f7a6",
"sha256:3a6b0a78af298d82323660df5497bcea0f0a4a25a0b003afd0ce5af049bd1f60",
"sha256:5385da8f3b801014504df0852bf83524599df890387a3c2b17b7caa3d78b1773",
"sha256:606d8afa07eef77280c2bf84335e24390055b478392e1975f96286d99d0cb424",
"sha256:69245b5b23bbf7fb242c9f8f08493e9ecd7711f063259aefffaeb90595d62287",
"sha256:6f6d839ab09830d59b7fa8fb6917023d8cb5498ee1f1dbd82d37db78eb76bc99",
"sha256:730888475f5ac0e37c1de4bd05eeb799fdb742697867f524dc8a4cd74bcecc23",
"sha256:9819b5162ffc121b9e334923c685b0d0826154e41dfe70b2ede2ce29034c71d8",
"sha256:9e60ef9426efab601dd9aa120e4ff560f4461cf8442e9c0a2b92548d52800699",
"sha256:af5fbdde0690c7da68e841d7fc2632345d570768ea7406a9434446d7b33b0ee1",
"sha256:b64efdbdf3bbb1377562c179f167f3bf301251411eb5ac77dec6b7d32bcda463",
"sha256:bac5f444c118aeb456fac1b0b5d14c6a71ea2a42069b09c176f75e9bd4c186f6",
"sha256:bda9068aafb73859491e13b99b682bd299c1b5fd50644d697533775828a28ee0",
"sha256:d659517ca116e6750101a1326107d3479028c5191f0ecee3c7203c50f5b915b0",
"sha256:eddd3fb1f3e0f82e5915a899285a39ee34ce18fd25d89582bc89fc9fb16cd2c6"
"sha256:132eae51d6ef3ff4a8c47c393a4ef5ebf0d1aecc96880eb5d6c8ceab7017cc9b",
"sha256:18141c1484ab8784006c839be8b985cfc82a2e9725837b0ecfa0203f71c4e39d",
"sha256:2baf617f5bbbfe73fd8846463f5aeafc912b5ee247f410700245d68525ec584a",
"sha256:3d90063f2cbbe39177e9b4d888e45777012652d6110156845b828908c51ae462",
"sha256:4304b2218b842d610aa1a1d87e1dc9559597969acc62ce717ee4dfeaa44d7eee",
"sha256:4983ede548ffc3541bae49a82675996497348e55bafd1554dc4e4a5d6eda541a",
"sha256:5315f4509c1476718a4825f45a203b82d7fdf2a6f5f0c8f166435975b1c9f7d4",
"sha256:6cdfb1b49d5345f7c2b90d638822d16ba62dc82f7616e9b4caa10b72f3f16649",
"sha256:7b325f12635598c604690efd7a0197d0b94b7d7778498e76e0710cd582fd1c7a",
"sha256:8d3b0e3b8626615826f9a626548057c5275a9733512b137984a68ba1598d3d2f",
"sha256:8f8631160c79f53081bd23446525db0bc4c5616f78d04021e6e434b286493fd7",
"sha256:912de10965f3dc89da23936f1cc4ed60764f712e5fa603a09dd904f88c996760",
"sha256:b010c07b975fe853c65d7bbe9d4ac62f1c69086750a574f6292597763781ba18",
"sha256:c908c10505904c48081a5415a1e295d8403e353e0c14c42b6d67f8f97fae6616",
"sha256:c94dd3807c0c0610f7c76f078119f4ea48235a953512752b9175f9f98f5ae2bd",
"sha256:ce65dee7594a84c466e79d7fb7d3303e7295d16a83c22c7c4037071b059e2c21",
"sha256:eaa9cfcb221a8a4c2889be6f93da141ac777eb8819f077e1d09fb12d00a09a93",
"sha256:f3376bc31bad66d46d44b4e6522c5c21976bf9bca4ef5987bb2bf727f4506cbb",
"sha256:f9202fa138544e13a4ec1a6792c35834250a85958fde1251b6a22e07d1260ae7"
],
"version": "==1.3.1"
"version": "==1.3.5"
},
"ujson": {
"hashes": [
@@ -790,17 +858,17 @@
},
"urllib3": {
"hashes": [
"sha256:61bf29cada3fc2fbefad4fdf059ea4bd1b4a86d2b6d15e1c7c0b582b9752fe39",
"sha256:de9529817c93f27c8ccbfead6985011db27bd0ddfcdb2d86f3f663385c6a9c22"
"sha256:a53063d8b9210a7bdec15e7b272776b9d42b2fd6816401a0d43006ad2f9902db",
"sha256:d363e3607d8de0c220d31950a8f38b18d5ba7c0830facd71a1c6b1036b7ce06c"
],
"version": "==1.24.1"
"version": "==1.25.2"
},
"uvicorn": {
"hashes": [
"sha256:d700b65169820fc260f39402b7f966c178691daaa40cb376cad99d7cd737f772"
"sha256:c10da7a54a6552279870900c881a2f1726314e2dd6270d4d3f9251683c643783"
],
"index": "pypi",
"version": "==0.7.0b1"
"version": "==0.7.1"
},
"uvloop": {
"hashes": [

View File

@@ -43,6 +43,28 @@ The key features are:
<small>* estimation based on tests on an internal development team, building production applications.</small>
## Opinions
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"*Im over the moon excited about **FastAPI**. Its so fun!*"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
## Requirements
@@ -60,7 +82,7 @@ FastAPI stands on the shoulders of giants:
$ pip install fastapi
```
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">uvicorn</a>.
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>.
```bash
$ pip install uvicorn
@@ -198,7 +220,7 @@ def read_item(item_id: int, q: str = None):
@app.put("/items/{item_id}")
def create_item(item_id: int, item: Item):
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```

View File

@@ -236,6 +236,19 @@ It was one of the first extremely fast Python frameworks based on `asyncio`. It
That's why **FastAPI** is based on Starlette, as it is the fastest framework available (tested by third-party benchmarks).
### <a href="https://falconframework.org/" target="_blank">Falcon</a>
Falcon is another high performance Python framework, it is designed to be minimal, and work as the foundation of other frameworks like Hug.
It uses the previous standard for Python web frameworks (WSGI) which is synchronous, so it can't handle Websockets and other use cases. Nevertheless, it also has a very good performance.
It is designed to have functions that receive two parameters, one "request" and one "response". Then you "read" parts from the request, and "write" parts to the response. Because of this design, it is not possible to declare request parameters and bodies with standard Python type hints as function parameters.
So, data validation, serialization, and documentation, have to be done in code, not automatically. Or they have to be implemented as a framework on top of Falcon, like Hug. This same distinction happens in other frameworks that are inspired by Falcon's design, of having one request object and one response object as parameters.
!!! check "Inspired **FastAPI** to"
Find ways to get great performance.
### <a href="https://moltenframework.com/" target="_blank">Molten</a>
I discovered Molten in the first stages of building **FastAPI**. And it has quite similar ideas:
@@ -257,11 +270,34 @@ Routes are declared in a single place, using functions declared in other places
This actually inspired updating parts of Pydantic, to support the same validation declaration style (all this functionality is now already available in Pydantic).
### <a href="http://www.hug.rest/" target="_blank">Hug</a>
Hug was one of the first frameworks to implement the declaration of API parameter types using Python type hints. This was a great idea that inspired other tools to do the same.
It used custom types in its declarations instead of standard Python types, but it was still a huge step forward.
It also was one of the first frameworks to generate a custom schema declaring the whole API in JSON.
It was not based on a standard like OpenAPI and JSON Schema. So it wouldn't be straightforward to integrate it with other tools, like Swagger UI. But again, it was a very innovative idea.
It has an interesting, uncommon feature: using the same framework, it's possible to create APIs and also CLIs.
As it is based on the previous standard for synchronous Python web frameworks (WSGI), it can't handle Websockets and other things, although it still has high performance too.
!!! info
Hug was created by Timothy Crosley, the same creator of <a href="https://github.com/timothycrosley/isort" target="_blank">`isort`</a>, a great tool to automatically sort imports in Python files.
!!! check "Ideas inspired in **FastAPI**"
Hug inspired parts of APIStar, and was one of the tools I found most promising, alongside APIStar.
Hug helped inspiring **FastAPI** to use Python type hints to declare parameters, and to generate a schema defining the API automatically.
### <a href="https://github.com/encode/apistar" target="_blank">APIStar</a> (<= 0.5)
Right before deciding to build **FastAPI** I found **APIStar** server. It had almost everything I was looking for and had a great design.
It was actually the first implementation of a framework using Python type hints to declare parameters and requests that I ever saw (before NestJS and Molten).
It was one of the first implementations of a framework using Python type hints to declare parameters and requests that I ever saw (before NestJS and Molten). I found it more or less at the same time as Hug. But APIStar used the OpenAPI standard.
It had automatic data validation, data serialization and OpenAPI schema generation based on the same type hints in several places.

View File

@@ -1,4 +1,4 @@
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=a979de55-980d-4721-a46f-77298b3f3923&hw=ph&test=fortune&l=zijzen-7" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
Independent TechEmpower benchmarks show **FastAPI** applications running under Uvicorn as <a href="https://www.techempower.com/benchmarks/#section=test&runid=7464e520-0dc2-473d-bd34-dbdfd7e85911&hw=ph&test=query&l=zijzen-7" target="_blank">one of the fastest Python frameworks available</a>, only below Starlette and Uvicorn themselves (used internally by FastAPI). (*)
But when checking benchmarks and comparisons you should have the following in mind.

View File

@@ -1,4 +1,4 @@
It is recommended to use <a href="https://www.docker.com/" target="_blank">**Docker**</a> for security, replicability, development simplicity, etc.
You can use <a href="https://www.docker.com/" target="_blank">**Docker**</a> for deployment. It has several advantages like security, replicability, development simplicity, etc.
In this section you'll see instructions and links to guides to know how to:
@@ -237,7 +237,21 @@ The generated project has instructions to deploy it, doing it takes other 2 min.
You can deploy **FastAPI** directly without Docker too.
You just need to install <a href="https://www.uvicorn.org/" target="_blank">Uvicorn</a> (or any other ASGI server).
You just need to install an ASGI compatible server like:
* <a href="https://www.uvicorn.org/" target="_blank">Uvicorn</a>, a lightning-fast ASGI server, built on uvloop and httptools.
```bash
pip install uvicorn
```
* <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>, an ASGI server also compatible with HTTP/2.
```bash
pip install hypercorn
```
...or any other ASGI server.
And run your application the same way you have done in the tutorials, but without the `--reload` option, e.g.:
@@ -245,9 +259,15 @@ And run your application the same way you have done in the tutorials, but withou
uvicorn main:app --host 0.0.0.0 --port 80
```
or with Hypercorn:
```bash
hypercorn main:app --bind 0.0.0.0:80
```
You might want to set up some tooling to make sure it is restarted automatically if it stops.
You might also want to install <a href="https://gunicorn.org/" target="_blank">Gunicorn</a> and <a href="https://www.uvicorn.org/#running-with-gunicorn" target="_blank">use it as a manager for Uvicorn</a>.
You might also want to install <a href="https://gunicorn.org/" target="_blank">Gunicorn</a> and <a href="https://www.uvicorn.org/#running-with-gunicorn" target="_blank">use it as a manager for Uvicorn</a>, or use Hypercorn with multiple workers.
Making sure to fine-tune the number of workers, etc.

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 80 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 85 KiB

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -43,6 +43,28 @@ The key features are:
<small>* estimation based on tests on an internal development team, building production applications.</small>
## Opinions
"*[...] I'm using **FastAPI** a ton these days. [...] I'm actually planning to use it for all of my team's **ML services at Microsoft**. Some of them are getting integrated into the core **Windows** product and some **Office** products.*"
<div style="text-align: right; margin-right: 10%;">Kabir Khan - <strong>Microsoft</strong> <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank"><small>(ref)</small></a></div>
---
"*Im over the moon excited about **FastAPI**. Its so fun!*"
<div style="text-align: right; margin-right: 10%;">Brian Okken - <strong><a href="https://pythonbytes.fm/episodes/show/123/time-to-right-the-py-wrongs?time_in_sec=855" target="_blank">Python Bytes</a> podcast host</strong> <a href="https://twitter.com/brianokken/status/1112220079972728832" target="_blank"><small>(ref)</small></a></div>
---
"*Honestly, what you've built looks super solid and polished. In many ways, it's what I wanted **Hug** to be - it's really inspiring to see someone build that.*"
<div style="text-align: right; margin-right: 10%;">Timothy Crosley - <strong><a href="http://www.hug.rest/" target="_blank">Hug</a> creator</strong> <a href="https://news.ycombinator.com/item?id=19455465" target="_blank"><small>(ref)</small></a></div>
---
## Requirements
@@ -60,7 +82,7 @@ FastAPI stands on the shoulders of giants:
$ pip install fastapi
```
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">uvicorn</a>.
You will also need an ASGI server, for production such as <a href="http://www.uvicorn.org" target="_blank">Uvicorn</a> or <a href="https://gitlab.com/pgjones/hypercorn" target="_blank">Hypercorn</a>.
```bash
$ pip install uvicorn
@@ -198,7 +220,7 @@ def read_item(item_id: int, q: str = None):
@app.put("/items/{item_id}")
def create_item(item_id: int, item: Item):
def update_item(item_id: int, item: Item):
return {"item_name": item.name, "item_id": item_id}
```

View File

@@ -1,164 +1,355 @@
## Next release
## 0.25.0
* Add support for Pydantic's `include`, `exclude`, `by_alias`.
* Update documentation: [Response Model](https://fastapi.tiangolo.com/tutorial/response-model/#response_model_include-and-response_model_exclude).
* Add docs for: [Body - updates](https://fastapi.tiangolo.com/tutorial/body-updates/), using Pydantic's `skip_defaults`.
* Add method consistency tests.
* PR [#264](https://github.com/tiangolo/fastapi/pull/264).
* Add `CONTRIBUTING.md` file to GitHub, to help new contributors. PR [#255](https://github.com/tiangolo/fastapi/pull/255) by [@wshayes](https://github.com/wshayes).
* Add support for Pydantic's `skip_defaults`:
* There's a new *path operation decorator* parameter `response_model_skip_defaults`.
* The name of the parameter will most probably change in a future version to `response_skip_defaults`, `model_skip_defaults` or something similar.
* New [documentation section about using `response_model_skip_defaults`](https://fastapi.tiangolo.com/tutorial/response-model/#response-model-encoding-parameters).
* PR [#248](https://github.com/tiangolo/fastapi/pull/248) by [@wshayes](https://github.com/wshayes).
## 0.24.0
* Add support for WebSockets with dependencies and parameters.
* Support included for:
* `Depends`
* `Security`
* `Cookie`
* `Header`
* `Path`
* `Query`
* ...as these are compatible with the WebSockets protocol (e.g. `Body` is not).
* [Updated documentation for WebSockets](https://fastapi.tiangolo.com/tutorial/websockets/).
* PR [#178](https://github.com/tiangolo/fastapi/pull/178) by [@jekirl](https://github.com/jekirl).
* Upgrade the compatible version of Pydantic to `0.26.0`.
* This includes JSON Schema support for IP address and network objects, bug fixes, and other features.
* PR [#247](https://github.com/tiangolo/fastapi/pull/247) by [@euri10](https://github.com/euri10).
## 0.23.0
* Upgrade the compatible version of Starlette to `0.12.0`.
* This includes support for ASGI 3 (the latest version of the standard).
* It's now possible to use [Starlette's `StreamingResponse`](https://www.starlette.io/responses/#streamingresponse) with iterators, like [file-like](https://docs.python.org/3/glossary.html#term-file-like-object) objects (as those returned by `open()`).
* It's now possible to use the low level utility `iterate_in_threadpool` from `starlette.concurrency` (for advanced scenarios).
* PR [#243](https://github.com/tiangolo/fastapi/pull/243).
* Add OAuth2 redirect page for Swagger UI. This allows having delegated authentication in the Swagger UI docs. For this to work, you need to add `{your_origin}/docs/oauth2-redirect` to the allowed callbacks in your OAuth2 provider (in Auth0, Facebook, Google, etc).
* For example, during development, it could be `http://localhost:8000/docs/oauth2-redirect`.
* Have in mind that this callback URL is independent of whichever one is used by your frontend. You might also have another callback at `https://yourdomain.com/login/callback`.
* This is only to allow delegated authentication in the API docs with Swagger UI.
* PR [#198](https://github.com/tiangolo/fastapi/pull/198) by [@steinitzu](https://github.com/steinitzu).
* Make Swagger UI and ReDoc route handlers (*path operations*) be `async` functions instead of lambdas to improve performance. PR [#241](https://github.com/tiangolo/fastapi/pull/241) by [@Trim21](https://github.com/Trim21).
* Make Swagger UI and ReDoc URLs parameterizable, allowing to host and serve local versions of them and have offline docs. PR [#112](https://github.com/tiangolo/fastapi/pull/112) by [@euri10](https://github.com/euri10).
## 0.22.0
* Add support for `dependencies` parameter:
* A parameter in *path operation decorators*, for dependencies that should be executed but the return value is not important or not used in the *path operation function*.
* A parameter in the `.include_router()` method of FastAPI applications and routers, to include dependencies that should be executed in each *path operation* in a router.
* This is useful, for example, to require authentication or permissions in specific group of *path operations*.
* Different `dependencies` can be applied to different routers.
* These `dependencies` are run before the normal parameter dependencies. And normal dependencies are run too. They can be combined.
* Dependencies declared in a router are executed first, then the ones defined in *path operation decorators*, and then the ones declared in normal parameters. They are all combined and executed.
* All this also supports using `Security` with `scopes` in those `dependencies` parameters, for more advanced OAuth 2.0 security scenarios with scopes.
* New documentation about [dependencies in *path operation decorators*](https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-path-operation-decorators/).
* New documentation about [dependencies in the `include_router()` method](https://fastapi.tiangolo.com/tutorial/bigger-applications/#include-an-apirouter-with-a-prefix-tags-responses-and-dependencies).
* PR [#235](https://github.com/tiangolo/fastapi/pull/235).
* Fix OpenAPI documentation of Starlette URL convertors. Specially useful when using `path` convertors, to take a whole path as a parameter, like `/some/url/{p:path}`. PR [#234](https://github.com/tiangolo/fastapi/pull/234) by [@euri10](https://github.com/euri10).
* Make default parameter utilities exported from `fastapi` be functions instead of classes (the new functions return instances of those classes). To be able to override the return types and fix `mypy` errors in FastAPI's users' code. Applies to `Path`, `Query`, `Header`, `Cookie`, `Body`, `Form`, `File`, `Depends`, and `Security`. PR [#226](https://github.com/tiangolo/fastapi/pull/226) and PR [#231](https://github.com/tiangolo/fastapi/pull/231).
* Separate development scripts `test.sh`, `lint.sh`, and `format.sh`. PR [#232](https://github.com/tiangolo/fastapi/pull/232).
* Re-enable `black` formatting checks for Python 3.7. PR [#229](https://github.com/tiangolo/fastapi/pull/229) by [@zamiramir](https://github.com/zamiramir).
## 0.21.0
* On body parsing errors, raise `from` previous exception, to allow better introspection in logging code. PR [#192](https://github.com/tiangolo/fastapi/pull/195) by [@ricardomomm](https://github.com/ricardomomm).
* Use Python logger named "`fastapi`" instead of root logger. PR [#222](https://github.com/tiangolo/fastapi/pull/222) by [@euri10](https://github.com/euri10).
* Upgrade Pydantic to version 0.25. PR [#225](https://github.com/tiangolo/fastapi/pull/225) by [@euri10](https://github.com/euri10).
* Fix typo in routing. PR [#221](https://github.com/tiangolo/fastapi/pull/221) by [@djlambert](https://github.com/djlambert).
## 0.20.1
* Add typing information to package including file `py.typed`. PR [#209](https://github.com/tiangolo/fastapi/pull/209) by [@meadsteve](https://github.com/meadsteve).
* Add FastAPI bot for Gitter. To automatically announce new releases. PR [#189](https://github.com/tiangolo/fastapi/pull/189).
## 0.20.0
* Upgrade OAuth2:
* Upgrade Password flow using Bearer tokens to use the correct HTTP status code 401 `UNAUTHORIZED`, with `WWW-Authenticate` headers.
* Update, simplify, and improve all the [security docs](https://fastapi.tiangolo.com/tutorial/security/intro/).
* Add new `scope_str` to `SecurityScopes` and update docs: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
* Update docs, images, tests.
* PR [#188](https://github.com/tiangolo/fastapi/pull/188).
* Include [Hypercorn](https://gitlab.com/pgjones/hypercorn) as an alternative ASGI server in the docs. PR [#187](https://github.com/tiangolo/fastapi/pull/187).
* Add docs for [Static Files](https://fastapi.tiangolo.com/tutorial/static-files/) and [Templates](https://fastapi.tiangolo.com/tutorial/templates/). PR [#186](https://github.com/tiangolo/fastapi/pull/186).
* Add docs for handling [Response Cookies](https://fastapi.tiangolo.com/tutorial/response-cookies/) and [Response Headers](https://fastapi.tiangolo.com/tutorial/response-headers/). PR [#185](https://github.com/tiangolo/fastapi/pull/185).
* Fix typos in docs. PR [#176](https://github.com/tiangolo/fastapi/pull/176) by [@chdsbd](https://github.com/chdsbd).
## 0.19.0
* Rename _path operation decorator_ parameter `content_type` to `response_class`. PR [#183](https://github.com/tiangolo/fastapi/pull/183).
* Add docs:
* How to use the `jsonable_encoder` in [JSON compatible encoder](https://fastapi.tiangolo.com/tutorial/encoder/).
* How to [Return a Response directly](https://fastapi.tiangolo.com/tutorial/response-directly/).
* Update how to use a [Custom Response Class](https://fastapi.tiangolo.com/tutorial/custom-response/).
* PR [#184](https://github.com/tiangolo/fastapi/pull/184).
## 0.18.0
* Add docs for [HTTP Basic Auth](https://fastapi.tiangolo.com/tutorial/custom-response/). PR [#177](https://github.com/tiangolo/fastapi/pull/177).
* Upgrade HTTP Basic Auth handling with automatic headers (automatic browser login prompt). PR [#175](https://github.com/tiangolo/fastapi/pull/175).
* Update dependencies for security. PR [#174](https://github.com/tiangolo/fastapi/pull/174).
* Add docs for [Middleware](https://fastapi.tiangolo.com/tutorial/middleware/). PR [#173](https://github.com/tiangolo/fastapi/pull/173).
## 0.17.0
* Make Flit publish from CI. PR [#170](https://github.com/tiangolo/fastapi/pull/170).
* Add documentation about handling [CORS (Cross-Origin Resource Sharing)](https://fastapi.tiangolo.com/tutorial/cors/). PR [#169](https://github.com/tiangolo/fastapi/pull/169).
* By default, encode by alias. This allows using Pydantic `alias` parameters working by default. PR [#168](https://github.com/tiangolo/fastapi/pull/168).
## 0.16.0
* Upgrade _path operation_ `doctsring` parsing to support proper Markdown descriptions. New documentation at [Path Operation Configuration](https://fastapi.tiangolo.com/tutorial/path-operation-configuration/#description-from-docstring). PR [#163](https://github.com/tiangolo/fastapi/pull/163).
* Refactor internal usage of Pydantic to use correct data types. PR [#164](https://github.com/tiangolo/fastapi/pull/164).
* Upgrade Pydantic to version `0.23`. PR [#160](https://github.com/tiangolo/fastapi/pull/160) by [@euri10](https://github.com/euri10).
* Fix typo in Tutorial about Extra Models. PR [#159](https://github.com/tiangolo/fastapi/pull/159) by [@danielmichaels](https://github.com/danielmichaels).
* Fix [Query Parameters](https://fastapi.tiangolo.com/tutorial/query-params/) URL examples in docs. PR [#157](https://github.com/tiangolo/fastapi/pull/157) by [@hayata-yamamoto](https://github.com/hayata-yamamoto).
## 0.15.0
* Add support for multiple file uploads (as a single form field). New docs at: [Multiple file uploads](https://fastapi.tiangolo.com/tutorial/request-files/#multiple-file-uploads). PR [#158](https://github.com/tiangolo/fastapi/pull/158).
* Add docs for: [Additional Status Codes](https://fastapi.tiangolo.com/tutorial/additional-status-codes/). PR [#156](https://github.com/tiangolo/fastapi/pull/156).
## 0.14.0
* Improve automatically generated names of path operations in OpenAPI (in API docs). A function `read_items` instead of having a generated name "Read Items Get" will have "Read Items". PR [#155](https://github.com/tiangolo/fastapi/pull/155).
* Add docs for: [Testing **FastAPI**](https://fastapi.tiangolo.com/tutorial/testing/). PR [#151](https://github.com/tiangolo/fastapi/pull/151).
* Update `/docs` Swagger UI to enable deep linking. This allows sharing the URL pointing directly to the path operation documentation in the docs. PR [#148](https://github.com/tiangolo/fastapi/pull/148) by [@wshayes](https://github.com/wshayes).
* Update development dependencies, `Pipfile.lock`. PR [#150](https://github.com/tiangolo/fastapi/pull/150).
* Include Falcon and Hug in: [Alternatives, Inspiration and Comparisons](https://fastapi.tiangolo.com/alternatives/).
## 0.13.0
* Improve/upgrade OAuth2 scopes support with `SecurityScopes`:
* `SecurityScopes` can be declared as a parameter like `Request`, to get the scopes of all super-dependencies/dependants.
* Improve `Security` handling, merging scopes when declaring `SecurityScopes`.
* Allow using `SecurityBase` (like `OAuth2`) classes with `Depends` and still document them. `Security` now is needed only to declare `scopes`.
* Updated docs about: [OAuth2 with Password (and hashing), Bearer with JWT tokens](https://fastapi.tiangolo.com/tutorial/security/oauth2-jwt/).
* New docs about: [OAuth2 scopes](https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/).
* PR [#141](https://github.com/tiangolo/fastapi/pull/141).
## 0.12.1
* Fix bug: handling additional `responses` in `APIRouter.include_router()`. PR [#140](https://github.com/tiangolo/fastapi/pull/140).
* Fix typo in SQL tutorial. PR [#138](https://github.com/tiangolo/fastapi/pull/138) by [@mostaphaRoudsari](https://github.com/mostaphaRoudsari).
* Fix typos in section about nested models and OAuth2 with JWT. PR [#127](https://github.com/tiangolo/fastapi/pull/127) by [@mmcloud](https://github.com/mmcloud).
## 0.12.0
* Add additional `responses` parameter to _path operation decorators_ to extend responses in OpenAPI (and API docs).
* It also allows extending existing responses generated from `response_model`, declare other media types (like images), etc.
* The new documentation is here: [Additional Responses](https://fastapi.tiangolo.com/tutorial/additional-responses/).
* `responses` can also be added to `.include_router()`, the updated docs are here: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/#add-some-custom-tags-and-responses).
* PR [#97](https://github.com/tiangolo/fastapi/pull/97) originally initiated by [@barsi](https://github.com/barsi).
* Update `scripts/test-cov-html.sh` to allow passing extra parameters like `-vv`, for development.
## 0.11.0
* Add `auto_error` parameter to security utility functions. Allowing them to be optional. Also allowing to have multiple alternative security schemes that are then checked in a single dependency instead of each one verifying and returning the error to the client automatically when not satisfied. PR <a href="https://github.com/tiangolo/fastapi/pull/134" target="_blank">#134</a>.
* Add `auto_error` parameter to security utility functions. Allowing them to be optional. Also allowing to have multiple alternative security schemes that are then checked in a single dependency instead of each one verifying and returning the error to the client automatically when not satisfied. PR [#134](https://github.com/tiangolo/fastapi/pull/134).
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-middleware-to-handle-sessions" target="_blank">SQL Tutorial</a> to close database sessions even when there are exceptions. PR <a href="https://github.com/tiangolo/fastapi/pull/89" target="_blank">#89</a> by <a href="https://github.com/alexiri" target="_blank">@alexiri</a>.
* Update [SQL Tutorial](https://fastapi.tiangolo.com/tutorial/sql-databases/#create-a-middleware-to-handle-sessions) to close database sessions even when there are exceptions. PR [#89](https://github.com/tiangolo/fastapi/pull/89) by [@alexiri](https://github.com/alexiri).
* Fix duplicate dependency in `pyproject.toml`. PR <a href="https://github.com/tiangolo/fastapi/pull/128" target="_blank">#128</a> by <a href="https://github.com/zxalif" target="_blank">@zxalif</a>.
* Fix duplicate dependency in `pyproject.toml`. PR [#128](https://github.com/tiangolo/fastapi/pull/128) by [@zxalif](https://github.com/zxalif).
## 0.10.3
* Add Gitter chat, badge, links, etc. <a href="https://gitter.im/tiangolo/fastapi" target="_blank">https://gitter.im/tiangolo/fastapi
</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/117" target="_blank">#117</a>.
* Add Gitter chat, badge, links, etc. [https://gitter.im/tiangolo/fastapi](https://gitter.im/tiangolo/fastapi) . PR [#117](https://github.com/tiangolo/fastapi/pull/117).
* Add docs about <a href="https://fastapi.tiangolo.com/tutorial/extending-openapi/" target="_blank">Extending OpenAPI</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/126" target="_blank">#126</a>.
* Add docs about [Extending OpenAPI](https://fastapi.tiangolo.com/tutorial/extending-openapi/). PR [#126](https://github.com/tiangolo/fastapi/pull/126).
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR <a href="https://github.com/tiangolo/fastapi/pull/92" target="_blank">#92</a> by <a href="https://github.com/blueyed" target="_blank">@blueyed</a>.
* Make Travis run Ubuntu Xenial (newer version) and Python 3.7 instead of Python 3.7-dev. PR [#92](https://github.com/tiangolo/fastapi/pull/92) by [@blueyed](https://github.com/blueyed).
* Fix duplicated param variable creation. PR <a href="https://github.com/tiangolo/fastapi/pull/123" target="_blank">#123</a> by <a href="https://github.com/yihuang" target="_blank">@yihuang</a>.
* Fix duplicated param variable creation. PR [#123](https://github.com/tiangolo/fastapi/pull/123) by [@yihuang](https://github.com/yihuang).
* Add note in <a href="https://fastapi.tiangolo.com/tutorial/response-model/" target="_blank">Response Model docs</a> about why using a function parameter instead of a function return type annotation. PR <a href="https://github.com/tiangolo/fastapi/pull/109" target="_blank">#109</a> by <a href="https://github.com/JHSaunders" target="_blank">@JHSaunders</a>.
* Add note in [Response Model docs](https://fastapi.tiangolo.com/tutorial/response-model/) about why using a function parameter instead of a function return type annotation. PR [#109](https://github.com/tiangolo/fastapi/pull/109) by [@JHSaunders](https://github.com/JHSaunders).
* Fix event docs (startup/shutdown) function name. PR <a href="https://github.com/tiangolo/fastapi/pull/105" target="_blank">#105</a> by <a href="https://github.com/stratosgear" target="_blank">@stratosgear</a>.
* Fix event docs (startup/shutdown) function name. PR [#105](https://github.com/tiangolo/fastapi/pull/105) by [@stratosgear](https://github.com/stratosgear).
## 0.10.2
* Fix OpenAPI (JSON Schema) for declarations of Python `Union` (JSON Schema `additionalProperties`). PR <a href="https://github.com/tiangolo/fastapi/pull/121" target="_blank">#121</a>.
* Fix OpenAPI (JSON Schema) for declarations of Python `Union` (JSON Schema `additionalProperties`). PR [#121](https://github.com/tiangolo/fastapi/pull/121).
* Update <a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" target="_blank">Background Tasks</a> with a note on Celery.
* Update [Background Tasks](https://fastapi.tiangolo.com/tutorial/background-tasks/) with a note on Celery.
* Document response models using unions and lists, updated at: <a href="https://fastapi.tiangolo.com/tutorial/extra-models/" target="_blank">Extra Models</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/108" target="_blank">#108</a>.
* Document response models using unions and lists, updated at: [Extra Models](https://fastapi.tiangolo.com/tutorial/extra-models/). PR [#108](https://github.com/tiangolo/fastapi/pull/108).
## 0.10.1
* Add docs and tests for <a href="https://github.com/encode/databases" target="_blank">encode/databases</a>. New docs at: <a href="https://fastapi.tiangolo.com/tutorial/async-sql-databases/" target="_blank">Async SQL (Relational) Databases</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/107" target="_blank">#107</a>.
* Add docs and tests for [encode/databases](https://github.com/encode/databases). New docs at: [Async SQL (Relational) Databases](https://fastapi.tiangolo.com/tutorial/async-sql-databases/). PR [#107](https://github.com/tiangolo/fastapi/pull/107).
## 0.10.0
* Add support for Background Tasks in *path operation functions* and dependencies. New documentation about <a href="https://fastapi.tiangolo.com/tutorial/background-tasks/" target="_blank">Background Tasks is here</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/103" target="_blank">#103</a>.
* Add support for Background Tasks in _path operation functions_ and dependencies. New documentation about [Background Tasks is here](https://fastapi.tiangolo.com/tutorial/background-tasks/). PR [#103](https://github.com/tiangolo/fastapi/pull/103).
* Add support for `.websocket_route()` in `APIRouter`. PR <a href="https://github.com/tiangolo/fastapi/pull/100" target="_blank">#100</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
* Add support for `.websocket_route()` in `APIRouter`. PR [#100](https://github.com/tiangolo/fastapi/pull/100) by [@euri10](https://github.com/euri10).
* New docs section about <a href="https://fastapi.tiangolo.com/tutorial/events/" target="_blank">Events: startup - shutdown</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/99" target="_blank">#99</a>.
* New docs section about [Events: startup - shutdown](https://fastapi.tiangolo.com/tutorial/events/). PR [#99](https://github.com/tiangolo/fastapi/pull/99).
## 0.9.1
* Document receiving <a href="https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-parameter-list-multiple-values" target="_blank">Multiple values with the same query parameter</a> and <a href="https://fastapi.tiangolo.com/tutorial/header-params/#duplicate-headers" target="_blank">Duplicate headers</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/95" target="_blank">#95</a>.
* Document receiving [Multiple values with the same query parameter](https://fastapi.tiangolo.com/tutorial/query-params-str-validations/#query-parameter-list-multiple-values) and [Duplicate headers](https://fastapi.tiangolo.com/tutorial/header-params/#duplicate-headers). PR [#95](https://github.com/tiangolo/fastapi/pull/95).
## 0.9.0
* Upgrade compatible Pydantic version to `0.21.0`. PR <a href="https://github.com/tiangolo/fastapi/pull/90" target="_blank">#90</a>.
* Upgrade compatible Pydantic version to `0.21.0`. PR [#90](https://github.com/tiangolo/fastapi/pull/90).
* Add documentation for: <a href="https://fastapi.tiangolo.com/tutorial/application-configuration/" target="_blank">Application Configuration</a>.
* Add documentation for: [Application Configuration](https://fastapi.tiangolo.com/tutorial/application-configuration/).
* Fix typo in docs. PR <a href="https://github.com/tiangolo/fastapi/pull/76" target="_blank">#76</a> by <a href="https://github.com/matthewhegarty" target="_blank">@matthewhegarty</a>.
* Fix typo in docs. PR [#76](https://github.com/tiangolo/fastapi/pull/76) by [@matthewhegarty](https://github.com/matthewhegarty).
* Fix link in "Deployment" to "Bigger Applications".
## 0.8.0
* Make development scripts executable. PR <a href="https://github.com/tiangolo/fastapi/pull/76" target="_blank">#76</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
* Make development scripts executable. PR [#76](https://github.com/tiangolo/fastapi/pull/76) by [@euri10](https://github.com/euri10).
* Add support for adding `tags` in `app.include_router()`. PR <a href="https://github.com/tiangolo/fastapi/pull/55" target="_blank">#55</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>. Documentation updated in the section: <a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/" target="_blank">Bigger Applications</a>.
* Add support for adding `tags` in `app.include_router()`. PR [#55](https://github.com/tiangolo/fastapi/pull/55) by [@euri10](https://github.com/euri10). Documentation updated in the section: [Bigger Applications](https://fastapi.tiangolo.com/tutorial/bigger-applications/).
* Update docs related to Uvicorn to use new `--reload` option from version `0.5.x`. PR <a href="https://github.com/tiangolo/fastapi/pull/74" target="_blank">#74</a>.
* Update docs related to Uvicorn to use new `--reload` option from version `0.5.x`. PR [#74](https://github.com/tiangolo/fastapi/pull/74).
* Update `isort` imports and scripts to be compatible with newer versions. PR <a href="https://github.com/tiangolo/fastapi/pull/75" target="_blank">#75</a>.
* Update `isort` imports and scripts to be compatible with newer versions. PR [#75](https://github.com/tiangolo/fastapi/pull/75).
## 0.7.1
* Update <a href="https://fastapi.tiangolo.com/async/#path-operation-functions" target="_blank">technical details about `async def` handling</a> with respect to previous frameworks. PR <a href="https://github.com/tiangolo/fastapi/pull/64" target="_blank">#64</a> by <a href="https://github.com/haizaar" target="_blank">@haizaar</a>.
* Update [technical details about `async def` handling](https://fastapi.tiangolo.com/async/#path-operation-functions) with respect to previous frameworks. PR [#64](https://github.com/tiangolo/fastapi/pull/64) by [@haizaar](https://github.com/haizaar).
* Add <a href="https://fastapi.tiangolo.com/deployment/#raspberry-pi-and-other-architectures" target="_blank">deployment documentation for Docker in Raspberry Pi</a> and other architectures.
* Add [deployment documentation for Docker in Raspberry Pi](https://fastapi.tiangolo.com/deployment/#raspberry-pi-and-other-architectures) and other architectures.
* Trigger Docker images build on Travis CI automatically. PR <a href="https://github.com/tiangolo/fastapi/pull/65" target="_blank">#65</a>.
* Trigger Docker images build on Travis CI automatically. PR [#65](https://github.com/tiangolo/fastapi/pull/65).
## 0.7.0
* Add support for `UploadFile` in `File` parameter annotations.
* This includes a file-like interface.
* Here's the updated documentation for declaring <a href="https://fastapi.tiangolo.com/tutorial/request-files/#file-parameters-with-uploadfile" target="_blank"> `File` parameters with `UploadFile`</a>.
* And here's the updated documentation for using <a href="https://fastapi.tiangolo.com/tutorial/request-forms-and-files/" target="_blank">`Form` parameters mixed with `File` parameters, supporting `bytes` and `UploadFile`</a> at the same time.
* PR <a href="https://github.com/tiangolo/fastapi/pull/63" target="_blank">#63</a>.
* Here's the updated documentation for declaring [`File` parameters with `UploadFile`](https://fastapi.tiangolo.com/tutorial/request-files/#file-parameters-with-uploadfile).
* And here's the updated documentation for using [`Form` parameters mixed with `File` parameters, supporting `bytes` and `UploadFile`](https://fastapi.tiangolo.com/tutorial/request-forms-and-files/) at the same time.
* PR [#63](https://github.com/tiangolo/fastapi/pull/63).
## 0.6.4
* Add <a href="https://fastapi.tiangolo.com/async/#very-technical-details" target="_blank">technical details about `async def` handling to docs</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/61" target="_blank">#61</a>.
* Add [technical details about `async def` handling to docs](https://fastapi.tiangolo.com/async/#very-technical-details). PR [#61](https://github.com/tiangolo/fastapi/pull/61).
* Add docs for <a href="https://fastapi.tiangolo.com/tutorial/debugging/" target="_blank">Debugging FastAPI applications in editors</a>.
* Add docs for [Debugging FastAPI applications in editors](https://fastapi.tiangolo.com/tutorial/debugging/).
* Clarify <a href="https://fastapi.tiangolo.com/deployment/#bigger-applications" target="_blank">Bigger Applications deployed with Docker</a>.
* Clarify [Bigger Applications deployed with Docker](https://fastapi.tiangolo.com/deployment/#bigger-applications).
* Fix typos in docs.
* Add section about <a href="https://fastapi.tiangolo.com/history-design-future/" target="_blank">History, Design and Future</a>.
* Add section about [History, Design and Future](https://fastapi.tiangolo.com/history-design-future/).
* Add docs for using <a href="https://fastapi.tiangolo.com/tutorial/websockets/" target="_blank">WebSockets with **FastAPI**</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/62" target="_blank">#62</a>.
* Add docs for using [WebSockets with **FastAPI**](https://fastapi.tiangolo.com/tutorial/websockets/). PR [#62](https://github.com/tiangolo/fastapi/pull/62).
## 0.6.3
* Add Favicons to docs. PR <a href="https://github.com/tiangolo/fastapi/pull/53" target="_blank">#53</a>.
* Add Favicons to docs. PR [#53](https://github.com/tiangolo/fastapi/pull/53).
## 0.6.2
* Introduce new project generator based on FastAPI and PostgreSQL: <a href="https://github.com/tiangolo/full-stack-fastapi-postgresql" target="_blank">https://github.com/tiangolo/full-stack-fastapi-postgresql</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/52" target="_blank">#52</a>.
* Introduce new project generator based on FastAPI and PostgreSQL: [https://github.com/tiangolo/full-stack-fastapi-postgresql](https://github.com/tiangolo/full-stack-fastapi-postgresql). PR [#52](https://github.com/tiangolo/fastapi/pull/52).
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">SQL tutorial with SQLAlchemy, using `Depends` to improve editor support and reduce code repetition</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/52" target="_blank">#52</a>.
* Update [SQL tutorial with SQLAlchemy, using `Depends` to improve editor support and reduce code repetition](https://fastapi.tiangolo.com/tutorial/sql-databases/). PR [#52](https://github.com/tiangolo/fastapi/pull/52).
* Improve middleware naming in tutorial for SQL with SQLAlchemy <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a>.
* Improve middleware naming in tutorial for SQL with SQLAlchemy [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/).
## 0.6.1
* Add docs for GraphQL: <a href="https://fastapi.tiangolo.com/tutorial/graphql/" target="_blank">https://fastapi.tiangolo.com/tutorial/graphql/</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/48" target="_blank">#48</a>.
* Add docs for GraphQL: [https://fastapi.tiangolo.com/tutorial/graphql/](https://fastapi.tiangolo.com/tutorial/graphql/). PR [#48](https://github.com/tiangolo/fastapi/pull/48).
## 0.6.0
* Update SQL with SQLAlchemy tutorial at <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a> using the new official `request.state`. PR <a href="https://github.com/tiangolo/fastapi/pull/45" target="_blank">#45</a>.
* Update SQL with SQLAlchemy tutorial at [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/) using the new official `request.state`. PR [#45](https://github.com/tiangolo/fastapi/pull/45).
* Upgrade Starlette to version `0.11.1` and add required compatibility changes. PR <a href="https://github.com/tiangolo/fastapi/pull/44" target="_blank">#44</a>.
* Upgrade Starlette to version `0.11.1` and add required compatibility changes. PR [#44](https://github.com/tiangolo/fastapi/pull/44).
## 0.5.1
* Add section about <a href="https://fastapi.tiangolo.com/help-fastapi/" target="_blank">helping and getting help with **FastAPI**</a>.
* Add section about [helping and getting help with **FastAPI**](https://fastapi.tiangolo.com/help-fastapi/).
* Add note about <a href="https://fastapi.tiangolo.com/tutorial/path-params/#order-matters" target="_blank">path operations order in docs</a>.
* Add note about [path operations order in docs](https://fastapi.tiangolo.com/tutorial/path-params/#order-matters).
* Update <a href="https://fastapi.tiangolo.com/tutorial/handling-errors/" target="_blank">section about error handling</a> with more information and make relation with Starlette error handling utilities more explicit. PR <a href="https://github.com/tiangolo/fastapi/pull/41" target="_blank">#41</a>.
* Update [section about error handling](https://fastapi.tiangolo.com/tutorial/handling-errors/) with more information and make relation with Starlette error handling utilities more explicit. PR [#41](https://github.com/tiangolo/fastapi/pull/41).
* Add <a href="" target="_blank">Development - Contributing section to the docs</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/42" target="_blank">#42</a>.
* Add <a href="" target="_blank">Development - Contributing section to the docs</a>. PR [#42](https://github.com/tiangolo/fastapi/pull/42).
## 0.5.0
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: <a href="https://fastapi.tiangolo.com/tutorial/handling-errors/" target="_blank">https://fastapi.tiangolo.com/tutorial/handling-errors/</a>. PR <a href="https://github.com/tiangolo/fastapi/pull/35" target="_blank">#35</a>.
* Add new `HTTPException` with support for custom headers. With new documentation for handling errors at: [https://fastapi.tiangolo.com/tutorial/handling-errors/](https://fastapi.tiangolo.com/tutorial/handling-errors/). PR [#35](https://github.com/tiangolo/fastapi/pull/35).
* Add <a href="https://fastapi.tiangolo.com/tutorial/using-request-directly/" target="_blank">documentation to use Starlette `Request` object</a> directly. Check <a href="https://github.com/tiangolo/fastapi/pull/25" target="_blank">#25</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
* Add [documentation to use Starlette `Request` object](https://fastapi.tiangolo.com/tutorial/using-request-directly/) directly. Check [#25](https://github.com/tiangolo/fastapi/pull/25) by [@euri10](https://github.com/euri10).
* Add issue templates to simplify reporting bugs, getting help, etc: <a href="https://github.com/tiangolo/fastapi/pull/34" target="_blank">#34</a>.
* Add issue templates to simplify reporting bugs, getting help, etc: [#34](https://github.com/tiangolo/fastapi/pull/34).
* Update example for the SQLAlchemy tutorial at <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">https://fastapi.tiangolo.com/tutorial/sql-databases/</a> using middleware and database session attached to request.
* Update example for the SQLAlchemy tutorial at [https://fastapi.tiangolo.com/tutorial/sql-databases/](https://fastapi.tiangolo.com/tutorial/sql-databases/) using middleware and database session attached to request.
## 0.4.0
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at <a href="https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/" target="_blank">https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/</a>: <a href="https://github.com/tiangolo/fastapi/pull/26" target="_blank">#26</a> by <a href="https://github.com/kabirkhan" target="_blank">@kabirkhan</a>.
* Add `openapi_prefix`, support for reverse proxy and mounting sub-applications. See the docs at [https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/](https://fastapi.tiangolo.com/tutorial/sub-applications-proxy/): [#26](https://github.com/tiangolo/fastapi/pull/26) by [@kabirkhan](https://github.com/kabirkhan).
* Update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">docs/tutorial for SQLAlchemy</a> including note about *DB Browser for SQLite*.
* Update [docs/tutorial for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/) including note about _DB Browser for SQLite_.
## 0.3.0
* Fix/add SQLAlchemy support, including ORM, and update <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">docs for SQLAlchemy</a>: <a href="https://github.com/tiangolo/fastapi/pull/30" target="_blank">#30</a>.
* Fix/add SQLAlchemy support, including ORM, and update [docs for SQLAlchemy](https://fastapi.tiangolo.com/tutorial/sql-databases/): [#30](https://github.com/tiangolo/fastapi/pull/30).
## 0.2.1
* Fix `jsonable_encoder` for Pydantic models with `Config` but without `json_encoders`: <a href="https://github.com/tiangolo/fastapi/pull/29" target="_blank">#29</a>.
* Fix `jsonable_encoder` for Pydantic models with `Config` but without `json_encoders`: [#29](https://github.com/tiangolo/fastapi/pull/29).
## 0.2.0
* Fix typos in Security section: <a href="https://github.com/tiangolo/fastapi/pull/24" target="_blank">#24</a> by <a href="https://github.com/kkinder" target="_blank">@kkinder</a>.
* Fix typos in Security section: [#24](https://github.com/tiangolo/fastapi/pull/24) by [@kkinder](https://github.com/kkinder).
* Add support for Pydantic custom JSON encoders: <a href="https://github.com/tiangolo/fastapi/pull/21" target="_blank">#21</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
* Add support for Pydantic custom JSON encoders: [#21](https://github.com/tiangolo/fastapi/pull/21) by [@euri10](https://github.com/euri10).
## 0.1.19
* Upgrade Starlette version to the current latest `0.10.1`: <a href="https://github.com/tiangolo/fastapi/pull/17" target="_blank">#17</a> by <a href="https://github.com/euri10" target="_blank">@euri10</a>.
* Upgrade Starlette version to the current latest `0.10.1`: [#17](https://github.com/tiangolo/fastapi/pull/17) by [@euri10](https://github.com/euri10).

View File

@@ -0,0 +1,23 @@
from fastapi import FastAPI
from pydantic import BaseModel
from starlette.responses import JSONResponse
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get("/items/{item_id}", response_model=Item, responses={404: {"model": Message}})
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
else:
return JSONResponse(status_code=404, content={"message": "Item not found"})

View File

@@ -0,0 +1,28 @@
from fastapi import FastAPI
from pydantic import BaseModel
from starlette.responses import FileResponse
class Item(BaseModel):
id: str
value: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
200: {
"content": {"image/png": {}},
"description": "Return the JSON item or an image.",
}
},
)
async def read_item(item_id: str, img: bool = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}

View File

@@ -0,0 +1,37 @@
from fastapi import FastAPI
from pydantic import BaseModel
from starlette.responses import JSONResponse
class Item(BaseModel):
id: str
value: str
class Message(BaseModel):
message: str
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={
404: {"model": Message, "description": "The item was not found"},
200: {
"description": "Item requested by ID",
"content": {
"application/json": {
"example": {"id": "bar", "value": "The bar tenders"}
}
},
},
},
)
async def read_item(item_id: str):
if item_id == "foo":
return {"id": "foo", "value": "there goes my hero"}
else:
return JSONResponse(status_code=404, content={"message": "Item not found"})

View File

@@ -0,0 +1,30 @@
from fastapi import FastAPI
from pydantic import BaseModel
from starlette.responses import FileResponse
class Item(BaseModel):
id: str
value: str
responses = {
404: {"description": "Item not found"},
302: {"description": "The item was moved"},
403: {"description": "Not enough privileges"},
}
app = FastAPI()
@app.get(
"/items/{item_id}",
response_model=Item,
responses={**responses, 200: {"content": {"image/png": {}}}},
)
async def read_item(item_id: str, img: bool = None):
if img:
return FileResponse("image.png", media_type="image/png")
else:
return {"id": "foo", "value": "there goes my hero"}

View File

@@ -0,0 +1,20 @@
from fastapi import Body, FastAPI
from starlette.responses import JSONResponse
from starlette.status import HTTP_201_CREATED
app = FastAPI()
items = {"foo": {"name": "Fighters", "size": 6}, "bar": {"name": "Tenders", "size": 3}}
@app.put("/items/{item_id}")
async def upsert_item(item_id: str, name: str = Body(None), size: int = Body(None)):
if item_id in items:
item = items[item_id]
item["name"] = name
item["size"] = size
return item
else:
item = {"name": name, "size": size}
items[item_id] = item
return JSONResponse(status_code=HTTP_201_CREATED, content=item)

View File

View File

@@ -0,0 +1,8 @@
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}

View File

@@ -0,0 +1,11 @@
from starlette.testclient import TestClient
from .main import app
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}

View File

@@ -0,0 +1,18 @@
from fastapi import FastAPI
from starlette.testclient import TestClient
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
client = TestClient(app)
def test_read_main():
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}

View File

@@ -0,0 +1,31 @@
from fastapi import FastAPI
from starlette.testclient import TestClient
from starlette.websockets import WebSocket
app = FastAPI()
@app.get("/")
async def read_main():
return {"msg": "Hello World"}
@app.websocket_route("/ws")
async def websocket(websocket: WebSocket):
await websocket.accept()
await websocket.send_json({"msg": "Hello WebSocket"})
await websocket.close()
def test_read_main():
client = TestClient(app)
response = client.get("/")
assert response.status_code == 200
assert response.json() == {"msg": "Hello World"}
def test_websocket():
client = TestClient(app)
with client.websocket_connect("/ws") as websocket:
data = websocket.receive_json()
assert data == {"msg": "Hello WebSocket"}

View File

@@ -0,0 +1,24 @@
from fastapi import FastAPI
from starlette.testclient import TestClient
app = FastAPI()
items = {}
@app.on_event("startup")
async def startup_event():
items["foo"] = {"name": "Fighters"}
items["bar"] = {"name": "Tenders"}
@app.get("/items/{item_id}")
async def read_items(item_id: str):
return items[item_id]
def test_read_items():
with TestClient(app) as client:
response = client.get("/items/foo")
assert response.status_code == 200
assert response.json() == {"name": "Fighters"}

View File

@@ -1,8 +1,20 @@
from fastapi import FastAPI
from fastapi import Depends, FastAPI, Header, HTTPException
from .routers import items, users
app = FastAPI()
async def get_token_header(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
app.include_router(users.router)
app.include_router(items.router, prefix="/items", tags=["items"])
app.include_router(
items.router,
prefix="/items",
tags=["items"],
dependencies=[Depends(get_token_header)],
responses={404: {"description": "Not found"}},
)

View File

@@ -1,4 +1,4 @@
from fastapi import APIRouter
from fastapi import APIRouter, HTTPException
router = APIRouter()
@@ -11,3 +11,14 @@ async def read_items():
@router.get("/{item_id}")
async def read_item(item_id: str):
return {"name": "Fake Specific Item", "item_id": item_id}
@router.put(
"/{item_id}",
tags=["custom"],
responses={403: {"description": "Operation forbidden"}},
)
async def update_item(item_id: str):
if item_id != "foo":
raise HTTPException(status_code=403, detail="You can only update the item: foo")
return {"item_id": item_id, "name": "The Fighters"}

View File

@@ -1,8 +1,7 @@
from typing import Set
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
from pydantic import BaseModel, UrlStr
app = FastAPI()

View File

@@ -1,8 +1,7 @@
from typing import List, Set
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
from pydantic import BaseModel, UrlStr
app = FastAPI()
@@ -18,7 +17,7 @@ class Item(BaseModel):
price: float
tax: float = None
tags: Set[str] = []
image: List[Image] = None
images: List[Image] = None
@app.put("/items/{item_id}")

View File

@@ -1,8 +1,7 @@
from typing import List, Set
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
from pydantic import BaseModel, UrlStr
app = FastAPI()
@@ -18,7 +17,7 @@ class Item(BaseModel):
price: float
tax: float = None
tags: Set[str] = []
image: List[Image] = None
images: List[Image] = None
class Offer(BaseModel):

View File

@@ -1,8 +1,7 @@
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
from pydantic.types import UrlStr
from pydantic import BaseModel, UrlStr
app = FastAPI()

View File

@@ -0,0 +1,34 @@
from typing import List
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str = None
description: str = None
price: float = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.put("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
update_item_encoded = jsonable_encoder(item)
items[item_id] = update_item_encoded
return update_item_encoded

View File

@@ -0,0 +1,37 @@
from typing import List
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str = None
description: str = None
price: float = None
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item)
async def read_item(item_id: str):
return items[item_id]
@app.patch("/items/{item_id}", response_model=Item)
async def update_item(item_id: str, item: Item):
stored_item_data = items[item_id]
stored_item_model = Item(**stored_item_data)
update_data = item.dict(skip_defaults=True)
updated_item = stored_item_model.copy(update=update_data)
items[item_id] = jsonable_encoder(updated_item)
return updated_item

View File

@@ -0,0 +1,19 @@
from fastapi import FastAPI
from starlette.middleware.cors import CORSMiddleware
app = FastAPI()
origins = [
"http://localhost.tiangolo.com",
"https://localhost.tiangolo.com",
"http:localhost",
"http:localhost:8080",
]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

View File

@@ -4,6 +4,6 @@ from starlette.responses import UJSONResponse
app = FastAPI()
@app.get("/items/", content_type=UJSONResponse)
@app.get("/items/", response_class=UJSONResponse)
async def read_items():
return [{"item_id": "Foo"}]

View File

@@ -4,7 +4,7 @@ from starlette.responses import HTMLResponse
app = FastAPI()
@app.get("/items/", content_type=HTMLResponse)
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return """
<html>

View File

@@ -18,6 +18,6 @@ def generate_html_response():
return HTMLResponse(content=html_content, status_code=200)
@app.get("/items/", content_type=HTMLResponse)
@app.get("/items/", response_class=HTMLResponse)
async def read_items():
return generate_html_response()

View File

@@ -1,21 +1,19 @@
from fastapi import Depends, FastAPI
from fastapi import Depends, FastAPI, Header, HTTPException
app = FastAPI()
class FixedContentQueryChecker:
def __init__(self, fixed_content: str):
self.fixed_content = fixed_content
def __call__(self, q: str = ""):
if q:
return self.fixed_content in q
return False
async def verify_token(x_token: str = Header(...)):
if x_token != "fake-super-secret-token":
raise HTTPException(status_code=400, detail="X-Token header invalid")
checker = FixedContentQueryChecker("bar")
async def verify_key(x_key: str = Header(...)):
if x_key != "fake-super-secret-key":
raise HTTPException(status_code=400, detail="X-Key header invalid")
return x_key
@app.get("/query-checker/")
async def read_query_check(fixed_content_included: bool = Depends(checker)):
return {"fixed_content_in_query": fixed_content_included}
@app.get("/items/", dependencies=[Depends(verify_token), Depends(verify_key)])
async def read_items():
return [{"item": "Foo"}, {"item": "Bar"}]

View File

@@ -0,0 +1,21 @@
from fastapi import Depends, FastAPI
app = FastAPI()
class FixedContentQueryChecker:
def __init__(self, fixed_content: str):
self.fixed_content = fixed_content
def __call__(self, q: str = ""):
if q:
return self.fixed_content in q
return False
checker = FixedContentQueryChecker("bar")
@app.get("/query-checker/")
async def read_query_check(fixed_content_included: bool = Depends(checker)):
return {"fixed_content_in_query": fixed_content_included}

View File

@@ -0,0 +1,22 @@
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
fake_db = {}
class Item(BaseModel):
title: str
timestamp: datetime
description: str = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
fake_db[id] = json_compatible_item_data

View File

@@ -12,5 +12,5 @@ async def startup_event():
@app.get("/items/{item_id}")
async def read_item(item_id: str):
async def read_items(item_id: str):
return items[item_id]

View File

@@ -6,7 +6,7 @@ items = {"foo": "The Foo Wrestlers"}
@app.get("/items/{item_id}")
async def create_item(item_id: str):
async def read_item(item_id: str):
if item_id not in items:
raise HTTPException(status_code=404, detail="Item not found")
return {"item": items[item_id]}

View File

@@ -6,7 +6,7 @@ items = {"foo": "The Foo Wrestlers"}
@app.get("/items-header/{item_id}")
async def create_item_header(item_id: str):
async def read_item_header(item_id: str):
if item_id not in items:
raise HTTPException(
status_code=404,

View File

@@ -0,0 +1,15 @@
import time
from fastapi import FastAPI
from starlette.requests import Request
app = FastAPI()
@app.middleware("http")
async def add_process_time_header(request: Request, call_next):
start_time = time.time()
response = await call_next(request)
process_time = time.time() - start_time
response.headers["X-Process-Time"] = str(process_time)
return response

View File

@@ -18,11 +18,11 @@ class Item(BaseModel):
async def create_item(*, item: Item):
"""
Create an item with all the information:
* name: each item must have a name
* description: a long description
* price: required
* tax: if the item doesn't have tax, you can omit this
* tags: a set of unique tag strings for this item
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
"""
return item

View File

@@ -23,11 +23,11 @@ class Item(BaseModel):
async def create_item(*, item: Item):
"""
Create an item with all the information:
* name: each item must have a name
* description: a long description
* price: required
* tax: if the item doesn't have tax, you can omit this
* tags: a set of unique tag strings for this item
- **name**: each item must have a name
- **description**: a long description
- **price**: required
- **tax**: if the item doesn't have tax, you can omit this
- **tags**: a set of unique tag strings for this item
"""
return item

View File

@@ -0,0 +1,33 @@
from typing import List
from fastapi import FastAPI, File, UploadFile
from starlette.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(files: List[bytes] = File(...)):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: List[UploadFile] = File(...)):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)

View File

@@ -0,0 +1,12 @@
from fastapi import FastAPI
from starlette.responses import JSONResponse
app = FastAPI()
@app.post("/cookie/")
def create_cookie():
content = {"message": "Come to the dark side, we have cookies"}
response = JSONResponse(content=content)
response.set_cookie(key="fakesession", value="fake-cookie-session-value")
return response

View File

@@ -0,0 +1,21 @@
from datetime import datetime
from fastapi import FastAPI
from fastapi.encoders import jsonable_encoder
from pydantic import BaseModel
from starlette.responses import JSONResponse
class Item(BaseModel):
title: str
timestamp: datetime
description: str = None
app = FastAPI()
@app.put("/items/{id}")
def update_item(id: str, item: Item):
json_compatible_item_data = jsonable_encoder(item)
return JSONResponse(content=json_compatible_item_data)

View File

@@ -0,0 +1,20 @@
from fastapi import FastAPI
from starlette.responses import Response
app = FastAPI()
@app.get("/legacy/")
def get_legacy_data():
data = """
<?xml version="1.0"?>
<shampoo>
<Header>
Apply shampoo here.
<Header>
<Body>
You'll have to use soap here.
</Body>
</shampoo>
"""
return Response(content=data, media_type="application/xml")

View File

@@ -0,0 +1,11 @@
from fastapi import FastAPI
from starlette.responses import JSONResponse
app = FastAPI()
@app.get("/headers/")
def get_headers():
content = {"message": "Hello World"}
headers = {"X-Cat-Dog": "alone in the world", "Content-Language": "en-US"}
return JSONResponse(content=content, headers=headers)

View File

@@ -1,4 +1,4 @@
from typing import Set
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
@@ -11,9 +11,9 @@ class Item(BaseModel):
description: str = None
price: float
tax: float = None
tags: Set[str] = []
tags: List[str] = []
@app.post("/items/", response_model=Item)
async def create_item(*, item: Item):
async def create_item(item: Item):
return item

View File

@@ -0,0 +1,26 @@
from typing import List
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = 10.5
tags: List[str] = []
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The bartenders", "price": 62, "tax": 20.2},
"baz": {"name": "Baz", "description": None, "price": 50.2, "tax": 10.5, "tags": []},
}
@app.get("/items/{item_id}", response_model=Item, response_model_skip_defaults=True)
async def read_item(item_id: str):
return items[item_id]

View File

@@ -0,0 +1,37 @@
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include={"name", "description"},
)
async def read_item_name(item_id: str):
return items[item_id]
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude={"tax"})
async def read_item_public_data(item_id: str):
return items[item_id]

View File

@@ -0,0 +1,37 @@
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = 10.5
items = {
"foo": {"name": "Foo", "price": 50.2},
"bar": {"name": "Bar", "description": "The Bar fighters", "price": 62, "tax": 20.2},
"baz": {
"name": "Baz",
"description": "There goes my baz",
"price": 50.2,
"tax": 10.5,
},
}
@app.get(
"/items/{item_id}/name",
response_model=Item,
response_model_include=["name", "description"],
)
async def read_item_name(item_id: str):
return items[item_id]
@app.get("/items/{item_id}/public", response_model=Item, response_model_exclude=["tax"])
async def read_item_public_data(item_id: str):
return items[item_id]

View File

@@ -1,4 +1,4 @@
from fastapi import FastAPI, Security
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
app = FastAPI()
@@ -7,5 +7,5 @@ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/token")
@app.get("/items/")
async def read_items(token: str = Security(oauth2_scheme)):
async def read_items(token: str = Depends(oauth2_scheme)):
return {"token": token}

View File

@@ -1,6 +1,6 @@
from typing import Optional
from fastapi import Depends, FastAPI, Security
from fastapi import Depends, FastAPI
from fastapi.security import OAuth2PasswordBearer
from pydantic import BaseModel
@@ -22,7 +22,7 @@ def fake_decode_token(token):
)
async def get_current_user(token: str = Security(oauth2_scheme)):
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_decode_token(token)
return user

View File

@@ -1,6 +1,7 @@
from fastapi import Depends, FastAPI, HTTPException, Security
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from pydantic import BaseModel
from starlette.status import HTTP_401_UNAUTHORIZED
fake_users_db = {
"johndoe": {
@@ -53,11 +54,13 @@ def fake_decode_token(token):
return user
async def get_current_user(token: str = Security(oauth2_scheme)):
async def get_current_user(token: str = Depends(oauth2_scheme)):
user = fake_decode_token(token)
if not user:
raise HTTPException(
status_code=400, detail="Invalid authentication credentials"
status_code=HTTP_401_UNAUTHORIZED,
detail="Invalid authentication credentials",
headers={"WWW-Authenticate": "Bearer"},
)
return user

View File

@@ -1,18 +1,17 @@
from datetime import datetime, timedelta
import jwt
from fastapi import Depends, FastAPI, HTTPException, Security
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from jwt import PyJWTError
from passlib.context import CryptContext
from pydantic import BaseModel
from starlette.status import HTTP_403_FORBIDDEN
from starlette.status import HTTP_401_UNAUTHORIZED
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
TOKEN_SUBJECT = "access"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
@@ -32,7 +31,7 @@ class Token(BaseModel):
token_type: str
class TokenPayload(BaseModel):
class TokenData(BaseModel):
username: str = None
@@ -83,20 +82,28 @@ def create_access_token(*, data: dict, expires_delta: timedelta = None):
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire, "sub": TOKEN_SUBJECT})
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(token: str = Security(oauth2_scheme)):
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
token_data = TokenPayload(**payload)
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except PyJWTError:
raise HTTPException(
status_code=HTTP_403_FORBIDDEN, detail="Could not validate credentials"
)
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
return user
@@ -107,18 +114,22 @@ async def get_current_active_user(current_user: User = Depends(get_current_user)
@app.post("/token", response_model=Token)
async def route_login_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=400, detail="Incorrect email or password")
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Incorrect username or password",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"username": form_data.username}, expires_delta=access_token_expires
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/users/me", response_model=User)
@app.get("/users/me/", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user

View File

@@ -0,0 +1,175 @@
from datetime import datetime, timedelta
from typing import List
import jwt
from fastapi import Depends, FastAPI, HTTPException, Security
from fastapi.security import (
OAuth2PasswordBearer,
OAuth2PasswordRequestForm,
SecurityScopes,
)
from jwt import PyJWTError
from passlib.context import CryptContext
from pydantic import BaseModel, ValidationError
from starlette.status import HTTP_401_UNAUTHORIZED
# to get a string like this run:
# openssl rand -hex 32
SECRET_KEY = "09d25e094faa6ca2556c818166b7a9563b93f7099f6f0f4caa6cf63b88e8d3e7"
ALGORITHM = "HS256"
ACCESS_TOKEN_EXPIRE_MINUTES = 30
fake_users_db = {
"johndoe": {
"username": "johndoe",
"full_name": "John Doe",
"email": "johndoe@example.com",
"hashed_password": "$2b$12$EixZaYVK1fsbw1ZfbX3OXePaWxn96p36WQoeG6Lruj3vjPGga31lW",
"disabled": False,
},
"alice": {
"username": "alice",
"full_name": "Alice Chains",
"email": "alicechains@example.com",
"hashed_password": "$2b$12$gSvqqUPvlXP2tfVFaWK1Be7DlH.PKZbv5H8KnzzVgXXbVxpva.pFm",
"disabled": True,
},
}
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: str = None
scopes: List[str] = []
class User(BaseModel):
username: str
email: str = None
full_name: str = None
disabled: bool = None
class UserInDB(User):
hashed_password: str
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
oauth2_scheme = OAuth2PasswordBearer(
tokenUrl="/token",
scopes={"me": "Read information about the current user.", "items": "Read items."},
)
app = FastAPI()
def verify_password(plain_password, hashed_password):
return pwd_context.verify(plain_password, hashed_password)
def get_password_hash(password):
return pwd_context.hash(password)
def get_user(db, username: str):
if username in db:
user_dict = db[username]
return UserInDB(**user_dict)
def authenticate_user(fake_db, username: str, password: str):
user = get_user(fake_db, username)
if not user:
return False
if not verify_password(password, user.hashed_password):
return False
return user
def create_access_token(*, data: dict, expires_delta: timedelta = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=15)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
async def get_current_user(
security_scopes: SecurityScopes, token: str = Depends(oauth2_scheme)
):
if security_scopes.scopes:
authenticate_value = f'Bearer scope="{security_scopes.scope_str}"'
else:
authenticate_value = f"Bearer"
credentials_exception = HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": authenticate_value},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_scopes = payload.get("scopes", [])
token_data = TokenData(scopes=token_scopes, username=username)
except (PyJWTError, ValidationError):
raise credentials_exception
user = get_user(fake_users_db, username=token_data.username)
if user is None:
raise credentials_exception
for scope in security_scopes.scopes:
if scope not in token_data.scopes:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Not enough permissions",
headers={"WWW-Authenticate": authenticate_value},
)
return user
async def get_current_active_user(
current_user: User = Security(get_current_user, scopes=["me"])
):
if current_user.disabled:
raise HTTPException(status_code=400, detail="Inactive user")
return current_user
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = authenticate_user(fake_users_db, form_data.username, form_data.password)
if not user:
raise HTTPException(status_code=400, detail="Incorrect username or password")
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username, "scopes": form_data.scopes},
expires_delta=access_token_expires,
)
return {"access_token": access_token, "token_type": "bearer"}
@app.get("/users/me/", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
return current_user
@app.get("/users/me/items/")
async def read_own_items(
current_user: User = Security(get_current_active_user, scopes=["items"])
):
return [{"item_id": "Foo", "owner": current_user.username}]
@app.get("/status/")
async def read_system_status(current_user: User = Depends(get_current_user)):
return {"status": "ok"}

View File

@@ -0,0 +1,11 @@
from fastapi import Depends, FastAPI
from fastapi.security import HTTPBasic, HTTPBasicCredentials
app = FastAPI()
security = HTTPBasic()
@app.get("/users/me")
def read_current_user(credentials: HTTPBasicCredentials = Depends(security)):
return {"username": credentials.username, "password": credentials.password}

View File

@@ -0,0 +1,22 @@
from fastapi import Depends, FastAPI, HTTPException
from fastapi.security import HTTPBasic, HTTPBasicCredentials
from starlette.status import HTTP_401_UNAUTHORIZED
app = FastAPI()
security = HTTPBasic()
def get_current_username(credentials: HTTPBasicCredentials = Depends(security)):
if credentials.username != "foo" or credentials.password != "password":
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Incorrect email or password",
headers={"WWW-Authenticate": "Basic"},
)
return credentials.username
@app.get("/users/me")
def read_current_user(username: str = Depends(get_current_username)):
return {"username": username}

View File

@@ -0,0 +1,6 @@
from fastapi import FastAPI
from starlette.staticfiles import StaticFiles
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")

View File

@@ -0,0 +1,3 @@
h1 {
color: green;
}

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>Item Details</title>
<link href="{{ url_for('static', path='/styles.css') }}" rel="stylesheet">
</head>
<body>
<h1>Item ID: {{ id }}</h1>
</body>
</html>

View File

@@ -0,0 +1,16 @@
from fastapi import FastAPI
from starlette.requests import Request
from starlette.staticfiles import StaticFiles
from starlette.templating import Jinja2Templates
app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")
@app.get("/items/{id}")
async def read_item(request: Request, id: str):
return templates.TemplateResponse("item.html", {"request": request, "id": id})

View File

View File

@@ -44,10 +44,9 @@ async def get():
return HTMLResponse(html)
@app.websocket_route("/ws")
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(f"Message text was: {data}")
await websocket.close()

View File

@@ -0,0 +1,78 @@
from fastapi import Cookie, Depends, FastAPI, Header
from starlette.responses import HTMLResponse
from starlette.status import WS_1008_POLICY_VIOLATION
from starlette.websockets import WebSocket
app = FastAPI()
html = """
<!DOCTYPE html>
<html>
<head>
<title>Chat</title>
</head>
<body>
<h1>WebSocket Chat</h1>
<form action="" onsubmit="sendMessage(event)">
<label>Item ID: <input type="text" id="itemId" autocomplete="off" value="foo"/></label>
<button onclick="connect(event)">Connect</button>
<br>
<label>Message: <input type="text" id="messageText" autocomplete="off"/></label>
<button>Send</button>
</form>
<ul id='messages'>
</ul>
<script>
var ws = null;
function connect(event) {
var input = document.getElementById("itemId")
ws = new WebSocket("ws://localhost:8000/items/" + input.value + "/ws");
ws.onmessage = function(event) {
var messages = document.getElementById('messages')
var message = document.createElement('li')
var content = document.createTextNode(event.data)
message.appendChild(content)
messages.appendChild(message)
};
}
function sendMessage(event) {
var input = document.getElementById("messageText")
ws.send(input.value)
input.value = ''
event.preventDefault()
}
</script>
</body>
</html>
"""
@app.get("/")
async def get():
return HTMLResponse(html)
async def get_cookie_or_client(
websocket: WebSocket, session: str = Cookie(None), x_client: str = Header(None)
):
if session is None and x_client is None:
await websocket.close(code=WS_1008_POLICY_VIOLATION)
return session or x_client
@app.websocket("/items/{item_id}/ws")
async def websocket_endpoint(
websocket: WebSocket,
item_id: int,
q: str = None,
cookie_or_client: str = Depends(get_cookie_or_client),
):
await websocket.accept()
while True:
data = await websocket.receive_text()
await websocket.send_text(
f"Session Cookie or X-Client Header value is: {cookie_or_client}"
)
if q is not None:
await websocket.send_text(f"Query parameter q is: {q}")
await websocket.send_text(f"Message text was: {data}, for item ID: {item_id}")

View File

@@ -0,0 +1,235 @@
!!! warning
This is a rather advanced topic.
If you are starting with **FastAPI**, you might not need this.
You can declare additional responses, with additional status codes, media types, descriptions, etc.
Those additional responses will be included in the OpenAPI schema, so they will also appear in the API docs.
But for those additional responses you have to make sure you return a `Response` like `JSONResponse` directly, with your status code and content.
## Additional Response with `model`
You can pass to your *path operation decorators* a parameter `responses`.
It receives a `dict`, the keys are status codes for each response, like `200`, and the values are other `dict`s with the information for each of them.
Each of those response `dict`s can have a key `model`, containing a Pydantic model, just like `response_model`.
**FastAPI** will take that model, generate its JSON Schema and include it in the correct place in OpenAPI.
For example, to declare another response with a status code `404` and a Pydantic model `Message`, you can write:
```Python hl_lines="18 23"
{!./src/additional_responses/tutorial001.py!}
```
!!! note
Have in mind that you have to return the `JSONResponse` directly.
!!! info
The `model` key is not part of OpenAPI.
**FastAPI** will take the Pydantic model from there, generate the `JSON Schema`, and put it in the correct place.
The correct place is:
* In the key `content`, that has as value another JSON object (`dict`) that contains:
* A key with the media type, e.g. `application/json`, that contains as value another JSON object, that contains:
* A key `schema`, that has as the value the JSON Schema from the model, here's the correct place.
* **FastAPI** adds a reference here to the global JSON Schemas in another place in your OpenAPI instead of including it directly. This way, other applications and clients can use those JSON Schemas directly, provide better code generation tools, etc.
The generated responses in the OpenAPI for this *path operation* will be:
```JSON hl_lines="3 4 5 6 7 8 9 10 11 12"
{
"responses": {
"404": {
"description": "Additional Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Message"
}
}
}
},
"200": {
"description": "Successful Response",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Item"
}
}
}
},
"422": {
"description": "Validation Error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/HTTPValidationError"
}
}
}
}
}
}
```
The schemas are referenced to another place inside the OpenAPI schema:
```JSON hl_lines="4 5 6 7 8 9 10 11 12 13 14 15 16"
{
"components": {
"schemas": {
"Message": {
"title": "Message",
"required": [
"message"
],
"type": "object",
"properties": {
"message": {
"title": "Message",
"type": "string"
}
}
},
"Item": {
"title": "Item",
"required": [
"id",
"value"
],
"type": "object",
"properties": {
"id": {
"title": "Id",
"type": "string"
},
"value": {
"title": "Value",
"type": "string"
}
}
},
"ValidationError": {
"title": "ValidationError",
"required": [
"loc",
"msg",
"type"
],
"type": "object",
"properties": {
"loc": {
"title": "Location",
"type": "array",
"items": {
"type": "string"
}
},
"msg": {
"title": "Message",
"type": "string"
},
"type": {
"title": "Error Type",
"type": "string"
}
}
},
"HTTPValidationError": {
"title": "HTTPValidationError",
"type": "object",
"properties": {
"detail": {
"title": "Detail",
"type": "array",
"items": {
"$ref": "#/components/schemas/ValidationError"
}
}
}
}
}
}
}
```
## Additional media types for the main response
You can use this same `responses` parameter to add different media types for the same main response.
For example, you can add an additional media type of `image/png`, declaring that your *path operation* can return a JSON object (with media type `application/json`) or a PNG image:
```Python hl_lines="17 18 19 20 21 22 23 24 28"
{!./src/additional_responses/tutorial002.py!}
```
!!! note
Notice that you have to return the image using a `FileResponse` directly.
## Combining information
You can also combine response information from multiple places, including the `response_model`, `status_code`, and `responses` parameters.
You can declare a `response_model`, using the default status code `200` (or a custom one if you need), and then declare additional information for that same response in `responses`, directly in the OpenAPI schema.
**FastAPI** will keep the additional information from `responses`, and combine it with the JSON Schema from your model.
For example, you can declare a response with a status code `404` that uses a Pydantic model and has a custom `description`.
And a response with a status code `200` that uses your `response_model`, but includes a custom `example`:
```Python hl_lines="20 21 22 23 24 25 26 27 28 29 30 31"
{!./src/additional_responses/tutorial003.py!}
```
It will all be combined and included in your OpenAPI, and shown in the API docs:
<img src="/img/tutorial/additional-responses/image01.png">
## Combine predefined responses and custom ones
You might want to have some predefined responses that apply to many *path operations*, but you want to combine them with custom responses needed by each *path operation*.
For those cases, you can use the Python technique of "unpacking" a `dict` with `**dict_to_unpack`:
```Python
old_dict = {
"old key": "old value",
"second old key": "second old value",
}
new_dict = {**old_dict, "new key": "new value"}
```
Here, `new_dict` will contain all the key-value pairs from `old_dict` plus the new key-value pair:
```Python
{
"old key": "old value",
"second old key": "second old value",
"new key": "new value",
}
```
You can use that technique to re-use some predefined responses in your *path operations* and combine them with additional custom ones.
For example:
```Python hl_lines="11 12 13 14 15 24"
{!./src/additional_responses/tutorial004.py!}
```
## More information about OpenAPI responses
To see what exactly you can include in the responses, you can check these sections in the OpenAPI specification:
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responsesObject" target="_blank">OpenAPI Responses Object</a>, it includes the `Response Object`.
* <a href="https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject" target="_blank">OpenAPI Response Object</a>, you can include anything from this directly in each response inside your `responses` parameter. Including `description`, `headers`, `content` (inside of this is that you declare different media types and JSON Schemas), and `links`.

View File

@@ -0,0 +1,30 @@
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`, putting the content you return from your *path operation* inside of that `JSONResponse`.
It will use the default status code or the one you set in your *path operation*.
## Additional status codes
If you want to return additional status codes apart from the main one, you can do that by returning a `Response` directly, like a `JSONResponse`, and set the additional status code directly.
For example, let's say that you want to have a *path operation* that allows to update items, and returns HTTP status codes of 200 "OK" when successful.
But you also want it to accept new items. And when the items didn't exist before, it creates them, and returns an HTTP status code of 201 "Created".
To achieve that, import `JSONResponse`, and return your content there directly, setting the `status_code` that you want:
```Python hl_lines="2 20"
{!./src/additional_status_codes/tutorial001.py!}
```
!!! warning
When you return a `Response` directly, like in the example above, it will be returned directly.
It won't be serialized with a model, etc.
Make sure it has the data you want it to have, and that the values are valid JSON (if you are using `JSONResponse`).
## OpenAPI and API docs
If you return additional status codes and responses directly, they won't be included in the OpenAPI schema (the API docs), because FastAPI doesn't have a way to know before hand what you are going to return.
But you can document that in your code, using: <a href="https://fastapi.tiangolo.com/tutorial/additional-responses/" target="_blank">Additional Responses</a>.

View File

@@ -22,16 +22,15 @@ Let's say you have a file structure like this:
!!! tip
There are two `__init__.py` files: one in each directory or subdirectory.
This is what allows importing code from one file into another.
For example, in `app/main.py` you could have a line like:
```
from app.routers import items
```
* The `app` directory contains everything.
* This `app` directory has an empty file `app/__init__.py`.
* So, the `app` directory is a "Python package" (a collection of "Python modules").
@@ -103,7 +102,17 @@ 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 16"
```Python hl_lines="6 11"
{!./src/bigger_applications/app/routers/items.py!}
```
### Add some custom `tags`, `responses`, and `dependencies`
We are not adding the prefix `/items/` nor the `tags=["items"]` to add them later.
But we can add custom `tags` and `responses` that will be applied to a specific *path operation*:
```Python hl_lines="18 19"
{!./src/bigger_applications/app/routers/items.py!}
```
@@ -187,12 +196,11 @@ So, to be able to use both of them in the same file, we import the submodules di
{!./src/bigger_applications/app/main.py!}
```
### Include an `APIRouter`
Now, let's include the `router` from the submodule `users`:
```Python hl_lines="8"
```Python hl_lines="13"
{!./src/bigger_applications/app/main.py!}
```
@@ -211,15 +219,14 @@ It will include all the routes from that router as part of it.
!!! check
You don't have to worry about performance when including routers.
This will take microseconds and will only happen at startup.
So it won't affect performance.
### Include an `APIRouter` with a `prefix`, `tags`, `responses`, and `dependencies`
### Include an `APIRouter` with a prefix
Now, let's include the router form the `items` submodule.
Now, let's include the router from the `items` submodule.
But, remember that we were lazy and didn't add `/items/` nor `tags` to all the *path operations*?
@@ -237,9 +244,13 @@ async def read_item(item_id: str):
So, the prefix in this case would be `/items`.
And we can also add a list of `tags` that will be applied to all the *path operations* included in this router:
We can also add a list of `tags` that will be applied to all the *path operations* included in this router.
```Python hl_lines="9"
And we can add predefined `responses` that will be included in all the *path operations* too.
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.
```Python hl_lines="8 9 10 14 15 16 17 18 19 20"
{!./src/bigger_applications/app/main.py!}
```
@@ -250,21 +261,28 @@ The end result is that the item paths are now:
...as we intended.
And they are marked with a list of tags that contain a single string `"items"`.
* They will be marked with a list of tags that contain a single string `"items"`.
* The *path operation* that declared a `"custom"` tag will have both tags, `items` and `custom`.
* These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
* All of them will include the predefined `responses`.
* The *path operation* that declared a custom `403` response will have both the predefined responses (`404`) and the `403` declared in it directly.
* All these *path operations* will have the list of `dependencies` evaluated/executed before them.
* If you also declare dependencies in a specific *path operation*, **they will be executed too**.
* The router dependencies are executed first, then the <a href="https://fastapi.tiangolo.com/tutorial/dependencies/dependencies-in-decorator/" target="_blank">`dependencies` in the decorator</a>, and then the normal parameter dependencies.
* You can also add <a href="https://fastapi.tiangolo.com/tutorial/security/oauth2-scopes/" target="_blank">`Security` dependencies with `scopes`</a>.
These "tags" are especially useful for the automatic interactive documentation systems (using OpenAPI).
!!! tip
Having `dependencies` in a decorator can be used, for example, to require authentication for a whole group of *path operations*. Even if the dependencies are not added individually to each one of them.
!!! check
The `prefix` and `tags` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
The `prefix`, `tags`, `responses` and `dependencies` parameters are (as in many other cases) just a feature from **FastAPI** to help you avoid code duplication.
!!! tip
You could also add path operations directly, for example with: `@app.get(...)`.
Apart from `app.include_router()`, in the same **FastAPI** app.
It would still work the same.
Apart from `app.include_router()`, in the same **FastAPI** app.
It would still work the same.
!!! info "Very Technical Details"
**Note**: this is a very technical detail that you probably can **just skip**.

View File

@@ -120,7 +120,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 `UrlStr`:
```Python hl_lines="5 11"
```Python hl_lines="4 10"
{!./src/body_nested_models/tutorial005.py!}
```
@@ -130,7 +130,7 @@ The string will be checked to be a valid URL, and documented in JSON Schema / Op
You can also use Pydantic models as subtypes of `list`, `set`, etc:
```Python hl_lines="21"
```Python hl_lines="20"
{!./src/body_nested_models/tutorial006.py!}
```
@@ -167,7 +167,7 @@ This will expect (convert, validate, document, etc) a JSON body like:
You can define arbitrarily deeply nested models:
```Python hl_lines="10 15 21 24 28"
```Python hl_lines="9 14 20 23 27"
{!./src/body_nested_models/tutorial007.py!}
```
@@ -184,7 +184,7 @@ images: List[Image]
as in:
```Python hl_lines="16"
```Python hl_lines="15"
{!./src/body_nested_models/tutorial008.py!}
```

View File

@@ -22,12 +22,13 @@ You can then use `Schema` with model attributes:
`Schema` works the same way as `Query`, `Path` and `Body`, it has all the same parameters, etc.
!!! info
!!! note "Technical Details"
Actually, `Query`, `Path` and others you'll see next are subclasses of a common `Param` which is itself a subclass of Pydantic's `Schema`.
`Body` is also a subclass of `Schema` directly. And there are others you will see later that are subclasses of `Body`.
But remember that when you import `Query`, `Path` and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
!!! tip
Notice how each model's attribute with a type, default value and `Schema` has the same structure as a path operation function's parameter, with `Schema` instead of `Path`, `Query` and `Body`.

View File

@@ -0,0 +1,97 @@
## Update replacing with `PUT`
To update an item you can use the [HTTP `PUT`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PUT) operation.
You can use the `jsonable_encoder` to convert the input data to data that can be stored as JSON (e.g. with a NoSQL database). For example, converting `datetime` to `str`.
```Python hl_lines="30 31 32 33 34 35"
{!./src/body_updates/tutorial001.py!}
```
`PUT` is used to receive data that should replace the existing data.
### Warning about replacing
That means that if you want to update the item `bar` using `PUT` with a body containing:
```Python
{
"name": "Barz",
"price": 3,
"description": None,
}
```
because it doesn't include the already stored attribute `"tax": 20.2`, the input model would take the default value of `"tax": 10.5`.
And the data would be saved with that "new" `tax` of `10.5`.
## Partial updates with `PATCH`
You can also use the [HTTP `PATCH`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/PATCH) operation to *partially* update data.
This means that you can send only the data that you want to update, leaving the rest intact.
!!! Note
`PATCH` is less commonly used and known than `PUT`.
And many teams use only `PUT`, even for partial updates.
You are **free** to use them however you want, **FastAPI** doesn't impose any restrictions.
But this guide shows you, more or less, how they are intended to be used.
### Using Pydantic's `skip_defaults` parameter
If you want to receive partial updates, it's very useful to use the parameter `skip_defaults` in Pydantic's model's `.dict()`.
Like `item.dict(skip_defaults=True)`.
That would generate a `dict` with only the data that was set when creating the `item` model, excluding default values.
Then you can use this to generate a `dict` with only the data that was set, omitting default values:
```Python hl_lines="34"
{!./src/body_updates/tutorial002.py!}
```
### Using Pydantic's `update` parameter
Now, you can create a copy of the existing model using `.copy()`, and pass the `update` parameter with a `dict` containing the data to update.
Like `stored_item_model.copy(update=update_data)`:
```Python hl_lines="35"
{!./src/body_updates/tutorial002.py!}
```
### Partial updates recap
In summary, to apply partial updates you would:
* (Optionally) use `PATCH` instead of `PUT`.
* Retrieve the stored data.
* Put that data in a Pydantic model.
* Generate a `dict` without default values from the input model (using `skip_defaults`).
* This way you can update only the values actually set by the user, instead of overriding values already stored with default values in your model.
* Create a copy of the stored model, updating it's attributes with the received partial updates (using the `update` parameter).
* Convert the copied model to something that can be stored in your DB (for example, using the `jsonable_encoder`).
* This is comparable to using the model's `.dict()` method again, but it makes sure (and converts) the values to data types that can be converted to JSON, for example, `datetime` to `str`.
* Save the data to your DB.
* Return the updated model.
```Python hl_lines="30 31 32 33 34 35 36 37"
{!./src/body_updates/tutorial002.py!}
```
!!! tip
You can actually use this same technique with an HTTP `PUT` operation.
But the example here uses `PATCH` because it was created for these use cases.
!!! note
Notice that the input model is still validated.
So, if you want to receive partial updates that can omit all the attributes, you need to have a model with all the attributes marked as optional (with default values or `None`).
To distinguish from the models with all optional values for **updates** and models with required values for **creation**, you can use the ideas described in <a href="https://fastapi.tiangolo.com/tutorial/extra-models/" target="_blank">Extra Models</a>.

View File

@@ -18,9 +18,11 @@ The first value is the default value, you can pass all the extra validation or a
{!./src/cookie_params/tutorial001.py!}
```
!!! info
!!! note "Technical Details"
`Cookie` is a "sister" class of `Path` and `Query`. It also inherits from the same common `Param` class.
But remember that when you import `Query`, `Path`, `Cookie` and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
!!! info
To declare cookies, you need to use `Cookie`, because otherwise the parameters would be interpreted as query parameters.

55
docs/tutorial/cors.md Normal file
View File

@@ -0,0 +1,55 @@
<a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank">CORS or "Cross-Origin Resource Sharing"</a> refers to the situations when a frontend running in a browser has JavaScript code that communicates with a backend, and the backend is in a different "origin" than the frontend.
## Origin
An origin is the combination of protocol (`http`, `https`), domain (`myapp.com`, `localhost`, `localhost.tiangolo.com`), and port (`80`, `443`, `8080`).
So, all these are different origins:
* `http://localhost`
* `https://localhost`
* `http://localhost:8080`
Even if they are all in `localhost`, they use different protocols or ports, so, they are different "origins".
## Steps
So, let's say you have a frontend running in your browser at `http://localhost:8080`, and its JavaScript is trying to communicate with a backend running at `http://localhost` (because we don't specify a port, the browser will assume the default port `80`).
Then, the browser will send an HTTP `OPTIONS` request to the backend, and if the backend sends the appropriate headers authorizing the communication from this different origin (`http://localhost:8080`) then the browser will let the JavaScript in the frontend send its request to the backend.
To achieve this, the backend must have a list of "allowed origins".
In this case, it would have to include `http://localhost:8080` for the frontend to work correctly.
## Wildcards
It's also possible to declare the list as `"*"` (a "wildcard") to say that all are allowed.
But that will only allow certain types of communication, excluding everything that involves credentials: Cookies, Authorization headers like those used with Bearer Tokens, etc.
So, for everything to work correctly, it's better to specify explicitly the allowed origins.
## Use `CORSMiddleware`
You can configure it in your **FastAPI** application using Starlette's <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">`CORSMiddleware`</a>.
* Import it from Starlette.
* Create a list of allowed origins (as strings).
* Add it as a "middleware" to your **FastAPI** application.
You can also specify if your backend allows:
* Credentials (Authorization headers, Cookies, etc).
* Specific HTTP methods (`POST`, `PUT`) or all of them with the wildcard `"*"`.
* Specific HTTP headers or all of them with the wildcard `"*"`.
```Python hl_lines="2 6 7 8 9 10 11 13 14 15 16 17 18 19"
{!./src/cors/tutorial001.py!}
```
## More info
For more details of what you can specify in `CORSMiddleware`, check <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">Starlette's `CORSMiddleware` docs</a>.
For more info about <abbr title="Cross-Origin Resource Sharing">CORS</abbr>, check the <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS" target="_blank">Mozilla CORS documentation</a>.

View File

@@ -1,98 +1,88 @@
!!! warning
This is a rather advanced topic.
If you are starting with **FastAPI**, you might not need this.
By default, **FastAPI** will return the responses using Starlette's `JSONResponse`.
But you can override it.
You can override it by returning a `Response` directly, <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">as seen in a previous section</a>.
But if you return a `Response` directly, the data won't be automatically converted, and the documentation won't be automatically generated (for example, including the specific "media type", in the HTTP header `Content-Type`).
But you can also declare the `Response` that you want to be used, in the *path operation decorator*.
The contents that you return from your *path operation function* will be put inside of that `Response`.
And if that `Response` has a JSON media type (`application/json`), like is the case with the `JSONResponse` and `UJSONResponse`, the data you return will be automatically converted (and filtered) with any Pydantic `response_model` that you declared in the *path operation decorator*.
## Use `UJSONResponse`
For example, if you are squeezing performance, you can use `ujson` and set the response to be Starlette's `UJSONResponse`.
For example, if you are squeezing performance, you can install and use `ujson` and set the response to be Starlette's `UJSONResponse`.
### Import `UJSONResponse`
Import the `Response` class (sub-class) you want to use and declare it in the *path operation decorator*.
```Python hl_lines="2"
```Python hl_lines="2 7"
{!./src/custom_response/tutorial001.py!}
```
!!! note
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
### Make your path operation use it
Make your path operation use `UJSONResponse` as the response class using the parameter `content_type`:
```Python hl_lines="7"
{!./src/custom_response/tutorial001.py!}
```
!!! info
The parameter is called `content_type` because it will also be used to define the "media type" of the response.
The parameter `response_class` will also be used to define the "media type" of the response.
And will be documented as such in OpenAPI.
In this case, the HTTP header `Content-Type` will be set to `application/json`.
And it will be documented as such in OpenAPI.
## HTML Response
To return a response with HTML directly from **FastAPI**, use `HTMLResponse`.
### Import `HTMLResponse`
* Import `HTMLResponse`.
* Pass `HTMLResponse` as the parameter `content_type` of your path operation.
```Python hl_lines="2"
```Python hl_lines="2 7"
{!./src/custom_response/tutorial002.py!}
```
!!! note
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
### Define your `content_type` class
Pass `HTMLResponse` as the parameter `content_type` of your path operation:
```Python hl_lines="7"
{!./src/custom_response/tutorial002.py!}
```
!!! info
The parameter is called `content_type` because it will also be used to define the "media type" of the response.
The parameter `response_class` will also be used to define the "media type" of the response.
In this case, the HTTP header `Content-Type` will be set to `text/html`.
And it will be documented as such in OpenAPI.
### Return a Starlette `Response`
You can also override the response directly in your path operation.
If you return an object that is an instance of Starlette's `Response`, it will be used as the response directly.
As seen in <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">another section</a>, you can also override the response directly in your path operation, by returning it.
The same example from above, returning an `HTMLResponse`, could look like:
```Python hl_lines="7"
```Python hl_lines="2 7 19"
{!./src/custom_response/tutorial003.py!}
```
!!! info
Of course, the `Content-Type` header will come from the the `Response` object your returned.
!!! warning
A `Response` returned directly by your path operation function won't be documented in OpenAPI and won't be visible in the automatic interactive docs.
A `Response` returned directly by your path operation function won't be documented in OpenAPI (for example, the `Content-Type` won't be documented) and won't be visible in the automatic interactive docs.
!!! info
Of course, the actual `Content-Type` header, status code, etc, will come from the `Response` object your returned.
### Document in OpenAPI and override `Response`
If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the `content_type` parameter AND return a `Response` object.
If you want to override the response from inside of the function but at the same time document the "media type" in OpenAPI, you can use the `response_class` parameter AND return a `Response` object.
The `content_type` class will then be used only to document the OpenAPI path operation, but your `Response` will be used as is.
The `response_class` will then be used only to document the OpenAPI path operation, but your `Response` will be used as is.
#### Return an `HTMLResponse` directly
For example, it could be something like:
```Python hl_lines="7 23"
```Python hl_lines="7 23 21"
{!./src/custom_response/tutorial004.py!}
```
@@ -100,16 +90,10 @@ In this example, the function `generate_html_response()` already generates a Sta
By returning the result of calling `generate_html_response()`, you are already returning a `Response` that will override the default **FastAPI** behavior.
#### Declare `HTMLResponse` as `content_type`
But by declaring it also in the path operation decorator:
```Python hl_lines="21"
{!./src/custom_response/tutorial004.py!}
```
#### OpenAPI knows how to document it
...**FastAPI** will be able to document it in OpenAPI and in the interactive docs as HTML with `text/html`:
But as you passed the `HTMLResponse` in the `response_class`, **FastAPI** will know how to document it in OpenAPI and the interactive docs as HTML with `text/html`:
<img src="/img/tutorial/custom-response/image01.png">
## Additional documentation
You can also declare the media type and many other details in OpenAPI using `responses`: <a href="https://fastapi.tiangolo.com/tutorial/additional-responses/" target="_blank">Additional Responses in OpenAPI</a>.

View File

@@ -69,7 +69,7 @@ will not be executed.
## Run your code with your debugger
Because you are running the Uvicorn server directly from your code, you can call your Python program (your FastAPI application) directly form the debugger.
Because you are running the Uvicorn server directly from your code, you can call your Python program (your FastAPI application) directly from the debugger.
---

View File

@@ -22,7 +22,7 @@ Not the class itself (which is already a callable), but an instance of that clas
To do that, we declare a method `__call__`:
```Python hl_lines="10"
{!./src/dependencies/tutorial006.py!}
{!./src/dependencies/tutorial007.py!}
```
In this case, this `__call__` is what **FastAPI** will use to check for additional parameters and sub-dependencies, and this is what will be called to pass a value to the parameter in your *path operation function* later.
@@ -32,7 +32,7 @@ In this case, this `__call__` is what **FastAPI** will use to check for addition
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
```Python hl_lines="7"
{!./src/dependencies/tutorial006.py!}
{!./src/dependencies/tutorial007.py!}
```
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
@@ -42,7 +42,7 @@ In this case, **FastAPI** won't ever touch or care about `__init__`, we will use
We could create an instance of this class with:
```Python hl_lines="16"
{!./src/dependencies/tutorial006.py!}
{!./src/dependencies/tutorial007.py!}
```
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
@@ -60,7 +60,7 @@ checker(q="somequery")
...and pass whatever that returns as the value of the dependency in our path operation function as the parameter `fixed_content_included`:
```Python hl_lines="20"
{!./src/dependencies/tutorial006.py!}
{!./src/dependencies/tutorial007.py!}
```
!!! tip

View File

@@ -0,0 +1,60 @@
In some cases you don't really need the return value of a dependency inside your *path operation function*.
Or the dependency doesn't return a value.
But you still need it to be executed/solved.
For those cases, instead of declaring a *path operation function* parameter with `Depends`, you can add a `list` of `dependencies` to the *path operation decorator*.
## Add `dependencies` to the *path operation decorator*
The *path operation decorator* receives an optional argument `dependencies`.
It should be a `list` of `Depends()`:
```Python hl_lines="17"
{!./src/dependencies/tutorial006.py!}
```
These dependencies will be executed/solved the same way normal dependencies. But their value (if they return any) won't be passed to your *path operation function*.
!!! tip
Some editors check for unused function parameters, and show them as errors.
Using these `dependencies` in the *path operation decorator* you can make sure they are executed while avoiding editor/tooling errors.
It might also help avoiding confusion for new developers that see an un-used parameter in your code and could think it's unnecessary.
## Dependencies errors and return values
You can use the same dependency *functions* you use normally.
### Dependency requirements
They can declare request requirements (like headers) or other sub-dependencies:
```Python hl_lines="6 11"
{!./src/dependencies/tutorial006.py!}
```
### Raise exceptions
These dependencies can `raise` exceptions, the same as normal dependencies:
```Python hl_lines="8 13"
{!./src/dependencies/tutorial006.py!}
```
### Return values
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"
{!./src/dependencies/tutorial006.py!}
```
## Dependencies for a group of *path operations*
Later, when reading about how to <a href="https://fastapi.tiangolo.com/tutorial/bigger-applications/" target="_blank">structure bigger applications</a>, possibly with multiple files, you will learn how to declare a single `dependencies` parameter for a group of *path operations*.

32
docs/tutorial/encoder.md Normal file
View File

@@ -0,0 +1,32 @@
There are some cases where you might need to convert a data type (like a Pydantic model) to something compatible with JSON (like a `dict`, `list`, etc).
For example, if you need to store it in a database.
For that, **FastAPI** provides a `jsonable_encoder()` function.
## Using the `jsonable_encoder`
Let's imagine that you have a database `fake_db` that only receives JSON compatible data.
For example, it doesn't receive `datetime` objects, as those are not compatible with JSON.
So, a `datetime` object would have to be converted to a `str` containing the data in <a href="https://en.wikipedia.org/wiki/ISO_8601" target="_blank">ISO format</a>.
The same way, this database wouldn't receive a Pydantic model (an object with attributes), only a `dict`.
You can use `jsonable_encoder` for that.
It receives an object, like a Pydantic model, and returns a JSON compatible version:
```Python hl_lines="4 21"
{!./src/encoder/tutorial001.py!}
```
In this example, it would convert the Pydantic model to a `dict`, and the `datetime` to a `str`.
The result of calling it is something that can be encoded with the Python standard <a href="https://docs.python.org/3/library/json.html#json.dumps" target="_blank">`json.dumps()`</a>.
It doesn't return a large `str` containing the data in JSON format (as a string). It returns a Python standard data structure (e.g. a `dict`) with values and sub-values that are all compatible with JSON.
!!! note
`jsonable_encoder` is actually used by **FastAPI** internally to convert data. But it is useful in many other scenarios.

View File

@@ -3,7 +3,7 @@ Continuing with the previous example, it will be common to have more than one re
This is especially the case for user models, because:
* The **input model** needs to be able to have a password.
* The **output model** should do not have a password.
* The **output model** should not have a password.
* The **database model** would probably need to have a hashed password.
!!! danger

View File

@@ -18,9 +18,11 @@ The first value is the default value, you can pass all the extra validation or a
{!./src/header_params/tutorial001.py!}
```
!!! info
!!! note "Technical Details"
`Header` is a "sister" class of `Path`, `Query` and `Cookie`. It also inherits from the same common `Param` class.
But remember that when you import `Query`, `Path`, `Header`, and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
!!! info
To declare headers, you need to use `Header`, because otherwise the parameters would be interpreted as query parameters.

View File

@@ -0,0 +1,60 @@
You can add middleware to **FastAPI** applications.
A "middleware" is a function that works with every **request** before it is processed by any specific *path operation*. And also with every **response** before returning it.
* It takes each **request** that comes to your application.
* It can then do something to that **request** or run any needed code.
* Then it passes the **request** to be processed by the rest of the application (by some *path operation*).
* It then takes the **response** generated by the application (by some *path operation*).
* It can do something to that **response** or run any needed code.
* Then it returns the **response**.
## Create a middleware
To create a middleware you use the decorator `@app.middleware("http")` on top of a function.
The middleware function receives:
* The `request`.
* A function `call_next` that will receive the `request` as a parameter.
* This function will pass the `request` to the corresponding *path operation*.
* Then it returns the `response` generated by the corresponding *path operation*.
* You can then modify further the `response` before returning it.
```Python hl_lines="9 10 12 15"
{!./src/middleware/tutorial001.py!}
```
!!! tip
This technique is used in the tutorial about <a href="https://fastapi.tiangolo.com/tutorial/sql-databases/" target="_blank">SQL (Relational) Databases</a>.
!!! tip
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" target="_blank">using the 'X-' prefix</a>.
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your <a href="https://fastapi.tiangolo.com/tutorial/cors/" target="_blank">CORS configurations</a>, using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">Starlette's CORS docs</a>.
### Before and after the `response`
You can add code to be run with the `request`, before any *path operation* receives it.
And also after the `response` is generated, before returning it.
For example, you could add a custom header `X-Process-Time` containing the time in seconds that it took to process the request and generate a response:
```Python hl_lines="11 13 14"
{!./src/middleware/tutorial001.py!}
```
## Starlette's Middleware
You can also add any other <a href="https://www.starlette.io/middleware/" target="_blank">Starlette Middleware</a>.
These are classes instead of plain functions.
Including:
* `CORSMiddleware` (described in the next section).
* `GZipMiddleware`.
* `SentryMiddleware`.
* ...and others.

View File

@@ -42,6 +42,8 @@ You can add a `summary` and `description`:
As descriptions tend to be long and cover multiple lines, you can declare the path operation description in the function <abbr title="a multi-line string as the first expression inside a function (not assigned to any variable) used for documentation">docstring</abbr> and **FastAPI** will read it from there.
You can write <a href="https://en.wikipedia.org/wiki/Markdown" 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"
{!./src/path_operation_configuration/tutorial004.py!}
```
@@ -50,9 +52,6 @@ It will be used in the interactive docs:
<img src="/img/tutorial/path-operation-configuration/image02.png">
!!! info
OpenAPI specifies that descriptions can be written in Markdown syntax, but the interactive documentation systems included still don't support it at the time of writing this, although they have it in their plans.
## Response description
You can specify the response description with the parameter `response_description`:

View File

@@ -103,6 +103,17 @@ And you can also declare numeric validations:
* `le`: `l`ess than or `e`qual
!!! info
`Query`, `Path` and others you will see later are subclasses of a common `Param` class (that you don't need to use).
And all of them share the same all these same parameters of additional validation and metadata you have seen.
`Query`, `Path` and others you will see later subclasses of a common `Param` class (that you don't need to use).
And all of them share the same all these same parameters of additional validation and metadata you have seen.
!!! note "Technical Details"
When you import `Query`, `Path` and others from `fastapi`, they are actually functions.
That when called, return instances of classes of the same name.
So, you import `Query`, which is a function. And when you call it, it returns an instance of a class also named `Query`.
These functions are there (instead of just using the classes directly) so that your editor doesn't mark errors about their types.
That way you can use your normal editor and coding tools without having to add custom configurations to disregard those errors.

View File

@@ -81,31 +81,31 @@ You can also declare `bool` types, and they will be converted:
In this case, if you go to:
```
http://127.0.0.1:8000/items/?short=1
http://127.0.0.1:8000/items/foo?short=1
```
or
```
http://127.0.0.1:8000/items/?short=True
http://127.0.0.1:8000/items/foo?short=True
```
or
```
http://127.0.0.1:8000/items/?short=true
http://127.0.0.1:8000/items/foo?short=true
```
or
```
http://127.0.0.1:8000/items/?short=on
http://127.0.0.1:8000/items/foo?short=on
```
or
```
http://127.0.0.1:8000/items/?short=yes
http://127.0.0.1:8000/items/foo?short=yes
```
or any other case variation (uppercase, first letter in uppercase, etc), your function will see the parameter `short` with a `bool` value of `True`. Otherwise as `False`.

View File

@@ -19,6 +19,8 @@ Create file parameters the same way you would for `Body` or `Form`:
!!! info
`File` is a class that inherits directly from `Form`.
But remember that when you import `Query`, `Path`, `File` and others from `fastapi`, <a href="https://fastapi.tiangolo.com/tutorial/path-params-numeric-validations/#recap" target="_blank">those are actually functions that return classes of the same name</a>.
!!! info
To declare File bodies, you need to use `File`, because otherwise the parameters would be interpreted as query parameters or body (JSON) parameters.
@@ -43,7 +45,7 @@ Using `UploadFile` has several advantages over `bytes`:
* It uses a "spooled" file:
* A file stored in memory up to a maximum size limit, and after passing this limit it will be stored in disk.
* This means that it will work well for large files like images, videos, large binaries, etc. All without consuming all the memory.
* This means that it will work well for large files like images, videos, large binaries, etc. without consuming all the memory.
* You can get metadata from the uploaded file.
* It has a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" target="_blank">file-like</a> `async` interface.
* It exposes an actual Python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" target="_blank">`SpooledTemporaryFile`</a> object that you can pass directly to other libraries that expect a file-like object.
@@ -107,6 +109,27 @@ The way HTML forms (`<form></form>`) sends the data to the server normally uses
This is not a limitation of **FastAPI**, it's part of the HTTP protocol.
## Multiple file uploads
It's possible to upload several files at the same time.
They would be associated to the same "form field" sent using "form data".
To use that, declare a `List` of `bytes` or `UploadFile`:
```Python hl_lines="10 15"
{!./src/request_files/tutorial002.py!}
```
You will receive, as declared, a `list` of `bytes` or `UploadFile`s.
!!! note
Notice that, as of 2019-04-14, Swagger UI doesn't support multiple file uploads in the same form field. For more information, check <a href="https://github.com/swagger-api/swagger-ui/issues/4276" target="_blank">#4276</a> and <a href="https://github.com/swagger-api/swagger-ui/issues/3641" target="_blank">#3641</a>.
Nevertheless, **FastAPI** is already compatible with it, using the standard OpenAPI.
So, whenever Swagger UI supports multi-file uploads, or any other tools that supports OpenAPI, they will be compatible with **FastAPI**.
## Recap
Use `File` to declare files to be uploaded as input parameters (as form data).

View File

@@ -0,0 +1,13 @@
You can create (set) Cookies in your response.
To do that, you can create a response as described in <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">Return a Response directly</a>.
Then set Cookies in it, and then return it:
```Python hl_lines="10 11 12"
{!./src/response_cookies/tutorial001.py!}
```
## More info
To see all the available parameters and options, check the <a href="https://www.starlette.io/responses/#set-cookie" target="_blank">documentation in Starlette</a>.

View File

@@ -0,0 +1,63 @@
When you create a **FastAPI** *path operation* you can normally return any data from it: a `dict`, a `list`, a Pydantic model, a database model, etc.
By default, **FastAPI** would automatically convert that return value to JSON using the <a href="https://fastapi.tiangolo.com/tutorial/encoder/" target="_blank">`jsonable_encoder`</a>.
Then, behind the scenes, it would put that JSON-compatible data (e.g. a `dict`) inside of a Starlette `JSONResponse` that would be used to send the response to the client.
But you can return a `JSONResponse` directly from your *path operations*.
It might be useful, for example, to return custom headers or cookies.
## Starlette `Response`
In fact, you can return any <a href="https://www.starlette.io/responses/" target="_blank">Starlette `Response`</a> or any sub-class of it.
!!! tip
`JSONResponse` itself is a sub-class of `Response`.
And when you return a Starlette `Response`, **FastAPI** will pass it directly.
It won't do any data conversion with Pydantic models, it won't convert the contents to any type, etc.
This gives you a lot of flexibility. You can return any data type, override any data declaration or validation, etc.
## Using the `jsonable_encoder` in a `Response`
Because **FastAPI** doesn't do any change to a `Response` you return, you have to make sure it's contents are ready for it.
For example, you cannot put a Pydantic model in a `JSONResponse` without first converting it to a `dict` with all the data types (like `datetime`, `UUID`, etc) converted to JSON-compatible types.
For those cases, you can use the `jsonable_encoder` to convert your data before passing it to a response:
```Python hl_lines="4 6 20 21"
{!./src/response_directly/tutorial001.py!}
```
!!! note
Notice that you import it directly from `starlette.responses`, not from `fastapi`.
## Returning a custom `Response`
The example above shows all the parts you need, but it's not very useful yet, as you could have just returned the `item` directly, and **FastAPI** would put it in a `JSONResponse` for you, converting it to a `dict`, etc. All that by default.
Now, let's see how you could use that to return a custom response.
Let's say you want to return a response that is not available in the default <a href="https://www.starlette.io/responses/" target="_blank">Starlette `Response`s</a>.
Let's say that you want to return <a href="https://en.wikipedia.org/wiki/XML" target="_blank">XML</a>.
You could put your XML content in a string, put it in a Starlette Response, and return it:
```Python hl_lines="2 20"
{!./src/response_directly/tutorial002.py!}
```
## Notes
When you return a `Response` directly its data is not validated, converted (serialized), nor documented automatically.
But you can still <a href="tutorial/additional-responses/" target="_blank">document it</a>.
In the next sections you will see how to use/declare these custom `Response`s while still having automatic data conversion, documentation, etc.
You will also see how to use them to set response Headers and Cookies.

View File

@@ -0,0 +1,12 @@
You can add headers to your response.
Create a response as described in <a href="https://fastapi.tiangolo.com/tutorial/response-directly/" target="_blank">Return a Response directly</a> and pass the headers as an additional parameter:
```Python hl_lines="10 11 12"
{!./src/response_headers/tutorial001.py!}
```
!!! tip
Have in mind that custom proprietary headers can be added <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" target="_blank">using the 'X-' prefix</a>.
But if you have custom headers that you want a client in a browser to be able to see, you need to add them to your <a href="https://fastapi.tiangolo.com/tutorial/cors/" target="_blank">CORS configurations</a>, using the parameter `expose_headers` documented in <a href="https://www.starlette.io/middleware/#corsmiddleware" target="_blank">Starlette's CORS docs</a>.

View File

@@ -13,12 +13,14 @@ You can declare the model used for the response with the parameter `response_mod
!!! note
Notice that `response_model` is a parameter of the "decorator" method (`get`, `post`, etc). Not of your path operation function, like all the parameters and body.
It receives a standard Pydantic model and will:
It receives the same type you would declare for a Pydantic model attribute, so, it can be a Pydantic model, but it can also be, e.g. a `list` of Pydantic models, like `List[Item]`.
* Convert the output data to the type declarations of the model
* Validate the data
* Add a JSON Schema for the response, in the OpenAPI path operation
* Will be used by the automatic documentation systems
FastAPI will use this `response_model` to:
* Convert the output data to its type declaration.
* Validate the data.
* Add a JSON Schema for the response, in the OpenAPI path operation.
* Will be used by the automatic documentation systems.
But most importantly:
@@ -45,7 +47,7 @@ Now, whenever a browser is creating a user with a password, the API will return
In this case, it might not be a problem, because the user himself is sending the password.
But if we use the same model for another path operation, we could be sending the passwords of our users to every client.
But if we use the same model for another path operation, we could be sending our user's passwords to every client.
!!! danger
Never send the plain password of a user in a response.
@@ -82,6 +84,114 @@ And both models will be used for the interactive API documentation:
<img src="/img/tutorial/response-model/image02.png">
## Response Model encoding parameters
Your response model could have default values, like:
```Python hl_lines="11 13 14"
{!./src/response_model/tutorial004.py!}
```
* `description: str = None` has a default of `None`.
* `tax: float = None` has a default of `None`.
* `tags: List[str] = []` has a default of an empty list: `[]`.
but you might want to omit them from the result if they were not actually stored.
For example, if you have models with many optional attributes in a NoSQL database, but you don't want to send very long JSON responses full of default values.
### Use the `response_model_skip_defaults` parameter
You can set the *path operation decorator* parameter `response_model_skip_defaults=True`:
```Python hl_lines="24"
{!./src/response_model/tutorial004.py!}
```
and those default values won't be included in the response.
So, if you send a request to that *path operation* for the item with ID `foo`, the response (not including default values) will be:
```JSON
{
"name": "Foo",
"price": 50.2
}
```
!!! info
FastAPI uses Pydantic model's `.dict()` with <a href="https://pydantic-docs.helpmanual.io/#copying" target="_blank">its `skip_defaults` parameter</a> to achieve this.
#### Data with values for fields with defaults
But if your data has values for the model's fields with default values, like the item with ID `bar`:
```Python hl_lines="3 5"
{
"name": "Bar",
"description": "The bartenders",
"price": 62,
"tax": 20.2
}
```
they will be included in the response.
#### Data with the same values as the defaults
If the data has the same values as the default ones, like the item with ID `baz`:
```Python hl_lines="3 5 6"
{
"name": "Baz",
"description": None,
"price": 50.2,
"tax": 10.5,
"tags": []
}
```
FastAPI is smart enough (actually, Pydantic is smart enough) to realize that, even though `description`, `tax`, and `tags` have the same values as the defaults, they were set explicitly (instead of taken from the defaults).
So, they will be included in the JSON response.
!!! tip
Notice that the default values can be anything, not only `None`.
They can be a list (`[]`), a `float` of `10.5`, etc.
### `response_model_include` and `response_model_exclude`
You can also use the *path operation decorator* parameters `response_model_include` and `response_model_exclude`.
They take a `set` of `str` with the name of the attributes to include (omitting the rest) or to exclude (including the rest).
This can be used as a quick shortcut if you have only one Pydantic model and want to remove some data from the output.
!!! tip
But it is still recommended to use the ideas above, using multiple classes, instead of these parameters.
This is because the JSON Schema generated in your app's OpenAPI (and the docs) will still be the one for the complete model, even if you use `response_model_include` or `response_model_exclude` to omit some attributes.
```Python hl_lines="29 35"
{!./src/response_model/tutorial005.py!}
```
!!! tip
The syntax `{"name", "description"}` creates a `set` with those two values.
It is equivalent to `set(["name", "description"])`.
#### Using `list`s instead of `set`s
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"
{!./src/response_model/tutorial006.py!}
```
## Recap
Use the path operation decorator's parameter `response_model` to define response models and especially to ensure private data is filtered out.
Use `response_model_skip_defaults` to return only the values explicitly set.

View File

@@ -12,7 +12,7 @@ Let's use the tools provided by **FastAPI** to handle security.
## How it looks
But let's first just use the code and see how it works, and then we'll come back to understand what's happening.
Let's first just use the code and see how it works, and then we'll come back to understand what's happening.
## Create `main.py`
@@ -77,37 +77,14 @@ So, let's review it from that simplified point of view:
* The API checks that `username` and `password`, and responds with a "token".
* 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.
* And if the token is stolen, the risk is less. It is not like a permanent key that will work forever.
* So, the user will have to login again at some point later.
* And if the token is stolen, the risk is less. It is not like a permanent key that will work forever (in most of the cases).
* The frontend stores that token temporarily somewhere.
* The user clicks in the frontend to go to another section of the frontend web app.
* The frontend needs to fetch some more data from the API.
* But it needs authentication for that specific endpoint.
* So, to authenticate with our API, it sends a header `Authorization` with a value of `Bearer ` plus the token.
* If the token contains `foobar`, the content of the `Authorization` header would be: `Bearer foobar`.
* Note that although the header is case-insensitive (`Authorization` is the same as `authorization`), the value is not. So, `bearer foobar` would not be valid. It has to be `Bearer foobar`.
## **FastAPI**'s `Security`
### Import it
The same way **FastAPI** provides a `Depends`, there is a `Security` that you can import:
```Python hl_lines="1"
{!./src/security/tutorial001.py!}
```
### Use it
It is actually a subclass of `Depends`, and it has just one extra parameter that we'll see later.
But by using `Security` instead of `Depends`, **FastAPI** will know that it can use this dependency to define "security schemes" in OpenAPI.
```Python hl_lines="10"
{!./src/security/tutorial001.py!}
```
In this case, we have a `Security` definition (which at the same time is a dependency definition) that will provide a `str` that is assigned to the parameter `token`.
## **FastAPI**'s `OAuth2PasswordBearer`
@@ -146,13 +123,30 @@ It could be called as:
oauth2_scheme(some, parameters)
```
So, it can be used with `Security` (as it could be used with `Depends`).
So, it can be used with `Depends`.
### Use it
Now you can pass that `oauth2_scheme` in a dependency with `Depends`.
```Python hl_lines="10"
{!./src/security/tutorial001.py!}
```
This dependency will provide a `str` that is assigned to the parameter `token` of the *path operation function*.
**FastAPI** will know that it can use this dependency to define a "security scheme" in the OpenAPI schema (and the automatic API docs).
!!! info "Technical Details"
**FastAPI** will know that it can use the class `OAuth2PasswordBearer` (declared in a dependency) to define the security scheme in OpenAPI because it inherits from `fastapi.security.oauth2.OAuth2`, which in turn inherits from `fastapi.security.base.SecurityBase`.
All the security utilities that integrate with OpenAPI (and the automatic API docs) inherit from `SecurityBase`, that's how **FastAPI** can know how to integrate them in OpenAPI.
## What it does
It will go and look in the request for that `Authorization` header, check if the value is `Bearer ` plus some token, and will return the token as a `str`.
If it doesn't see an `Authorization` header, or the value doesn't have a `Bearer ` token, it will respond with a 403 status code error (`FORBIDDEN`) directly.
If it doesn't see an `Authorization` header, or the value doesn't have a `Bearer ` token, it will respond with a 401 status code error (`UNAUTHORIZED`) directly.
You don't even have to check if the token exists to return an error. You can be sure that if your function is executed, it will have a `str` in that token.

View File

@@ -24,13 +24,9 @@ Let's create a dependency `get_current_user`.
Remember that dependencies can have sub-dependencies?
And remember that `Security` is based on `Depends`?
`get_current_user` will have a dependency with the same `oauth2_scheme` we created before.
So, we can have sub-dependencies using `Security` too.
`get_current_user` will have a `Security` dependency with the same `oauth2_scheme` we created before.
The same as we were doing before in the path operation directly, our new dependency will receive a `token` as a `str` from the `Security` dependency:
The same as we were doing before in the path operation directly, our new dependency `get_current_user` will receive a `token` as a `str` from the sub-dependency `oauth2_scheme`:
```Python hl_lines="25"
{!./src/security/tutorial002.py!}
@@ -52,15 +48,6 @@ So now we can use the same `Depends` with our `get_current_user` in the path ope
{!./src/security/tutorial002.py!}
```
!!! info
Here you could actually use `Security` instead of depends too.
But it is not required.
The key point where you should use `Security` is when passing an instance of `OAuth2PasswordBearer`.
Because **FastAPI** will use the fact that you are using `Security` and that you are passing an instance of that class `OAuth2PasswordBearer` (that inherits from `SecurityBase`) to create all the security definitions in OpenAPI.
Notice that we declare the type of `current_user` as the Pydantic model `User`.
This will help us inside of the function with all the completion and type checks.
@@ -68,7 +55,7 @@ This will help us inside of the function with all the completion and type checks
!!! tip
You might remember that request bodies are also declared with Pydantic models.
Here **FastAPI** won't get confused because you are using `Depends` or `Security`.
Here **FastAPI** won't get confused because you are using `Depends`.
!!! check
The way this dependency system is designed allows us to have different dependencies (different "dependables") that all return a `User` model.
@@ -78,7 +65,7 @@ This will help us inside of the function with all the completion and type checks
## Other models
You can now get the current user directly in the path operation functions and deal with the security mechanisms at the **Dependency Injection** level, using `Security`.
You can now get the current user directly in the path operation functions and deal with the security mechanisms at the **Dependency Injection** level, using `Depends`.
And you can use any model or data for the security requirements (in this case, a Pydantic model `User`).
@@ -88,6 +75,10 @@ Do you want to have an `id` and `email` and not have any `username` in your mode
Do you want to just have a `str`? Or just a `dict`? Or a database class model instance directly? It all works the same way.
You actually don't have users that log in to your application but robots, bots, or other systems, that have just an access token? Again, it all works the same.
Just use any kind of model, any kind of class, any kind of database that you need for your application. **FastAPI** has you covered with the dependency injection system.
## Code size
@@ -97,7 +88,7 @@ But here's the key point.
The security and dependency injection stuff is written once.
And you can make it as complex as you want. And still, have it written only once, in a single place.
And you can make it as complex as you want. And still, have it written only once, in a single place. With all the flexibility.
But you can have thousands of endpoints (path operations) using the same security system.
@@ -115,6 +106,6 @@ You can now get the current user directly in your path operation function.
We are already halfway there.
We just need to add a path operation for the user / client to actually send the `username` and `password`.
We just need to add a path operation for the user/client to actually send the `username` and `password`.
That comes next.

View File

@@ -0,0 +1,40 @@
For the simplest cases, you can use HTTP Basic Auth.
In HTTP Basic Auth, the application expects a header that contains a username and a password.
If it doesn't receive it, it returns an HTTP 401 "Unauthorized" error.
And returns a header `WWW-Authenticate` with a value of `Basic`, and an optional `realm` parameter.
That tells the browser to show the integrated prompt for a username and password.
Then, when you type that username and password, the browser sends them in the header automatically.
## Simple HTTP Basic Auth
* Import `HTTPBAsic` and `HTTPBasicCredentials`.
* Create a "`security` scheme" using `HTTPBAsic`.
* Use that `security` with a dependency in your *path operation*.
* It returns an object of type `HTTPBasicCredentials`:
* It contains the `username` and `password` sent.
```Python hl_lines="2 6 10"
{!./src/security/tutorial006.py!}
```
When you try to open the URL for the first time (or click the "Execute" button in the docs) the browser will ask you for your username and password:
<img src="/img/tutorial/security/image12.png">
## Check the username
Here's a more complete example.
Use a dependency to check if the username and password are correct.
If 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="10 11 12 13 14 15 16 17 21"
{!./src/security/tutorial007.py!}
```

View File

@@ -20,7 +20,7 @@ It is quite an extensive specification and covers several complex use cases.
It includes ways to authenticate using a "third party".
That's what all the system with "login with Facebook, Google, Twitter, GitHub" use underneath.
That's what all the systems with "login with Facebook, Google, Twitter, GitHub" use underneath.
### OAuth 1
@@ -40,7 +40,7 @@ OpenID Connect is another specification, based on **OAuth2**.
It just extends OAuth2 specifying some things that are relatively ambiguous in OAuth2, to try to make it more interoperable.
For example, Google login used OpenID Connect (which underneath uses OAuth2).
For example, Google login uses OpenID Connect (which underneath uses OAuth2).
But Facebook login doesn't support OpenID Connect. It has its own flavor of OAuth2.
@@ -75,7 +75,7 @@ OpenAPI defines the following security schemes:
* HTTP Basic authentication.
* HTTP Digest, etc.
* `oauth2`: all the OAuth2 ways to handle security (called "flows").
* Several of these flows are appropriate for delegating the authentication to a third party (like Google, Facebook, Twitter, GitHub, etc):
* Several of these flows are appropriate for building an OAuth 2.0 authentication provider (like Google, Facebook, Twitter, GitHub, etc):
* `implicit`
* `clientCredentials`
* `authorizationCode`
@@ -84,10 +84,16 @@ OpenAPI defines the following security schemes:
* `openIdConnect`: has a way to define how to discover OAuth2 authentication data automatically.
* This automatic discovery is what is defined in the OpenID Connect specification.
!!! tip
Integrating other authentication/authorization providers like Google, Facebook, Twitter, GitHub, etc. is also possible and relatively easy.
The most complex problem is building an authentication/authorization provider like those, but **FastAPI** gives you the tools to do it easily, while doing the heavy lifting for you.
## **FastAPI** utilities
FastAPI provides several tools for each of these security schemes in the `fastapi.security` module, to simplify using these security mechanisms.
FastAPI provides several tools for each of these security schemes in the `fastapi.security` module that simplify using these security mechanisms.
In the next chapters you will see how to add security to your API in a very simple way, using the tools provided by **FastAPI**.
In the next chapters you will see how to add security to your API using those tools provided by **FastAPI**.
And you will also see how it gets automatically integrated into the interactive documentation system.

View File

@@ -1,4 +1,4 @@
Now that we have all the security flow, let's make the application actually secure, using JWT tokens and secure password hashing.
Now that we have all the security flow, let's make the application actually secure, using <abbr title="JSON Web Tokens">JWT</abbr> tokens and secure password hashing.
This code is something you can actually use in your application, save the password hashes in your database, etc.
@@ -8,7 +8,11 @@ We are going to start from where we left in the previous chapter and increment i
JWT means "JSON Web Tokens".
It's a standard to codify a JSON object in a long string.
It's a standard to codify a JSON object in a long dense string without spaces. It looks like this:
```
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```
It is not encrypted, so, anyone could recover the information from the contents.
@@ -16,13 +20,13 @@ But it's signed. So, when you receive a token that you emitted, you can verify t
That way, you can create a token with an expiration of, let's say, 1 week, and then, after a week, when the user comes back with the token, you know he's still signed into your system.
And after a week, the token will be expired. 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 signature would not match.
And after a week, the token will be expired. 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/" target="_blank">https://jwt.io</a>.
## Install `PyJWT`
We need to install `PyJWT` to generate and verity the JWT tokens in Python:
We need to install `PyJWT` to generate and verify the JWT tokens in Python:
```bash
pip install pyjwt
@@ -30,7 +34,7 @@ pip install pyjwt
## Password hashing
"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that look like gibberish.
"Hashing" means converting some content (a password in this case) into a sequence of bytes (just a string) that looks like gibberish.
Whenever you pass exactly the same content (exactly the same password) you get exactly the same gibberish.
@@ -46,7 +50,7 @@ So, the thief won't be able to try to use that password in another system (as ma
PassLib is a great Python package to handle password hashes.
It supports many secure hashing algorithms, and utilities to work with them.
It supports many secure hashing algorithms and utilities to work with them.
The recommended algorithm is "Bcrypt".
@@ -57,10 +61,11 @@ pip install passlib[bcrypt]
```
!!! tip
With `passlib`, you could even configure it to be able to read passwords created by **Django** (among many others).
With `passlib`, you could even configure it to be able to read passwords created by **Django**, a **Flask** security plug-in or many others.
So, you would be able to, for example, share the same data from a Django application in a database with a FastAPI application. Or gradually migrate a Django application using the same database.
And your users would be able to login from your Django app or from your **FastAPI** app, at the same time.
## Hash and verify the passwords
@@ -69,7 +74,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, deprecate old ones, but allow verifying them, etc.
The PassLib context also has functionality to use different hashing algorithms, including deprecate 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.
@@ -81,7 +86,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 50 57 58 61 62 71 72 73 74 75 76 77"
```Python hl_lines="7 39 56 57 60 61 70 71 72 73 74 75 76"
{!./src/security/tutorial004.py!}
```
@@ -94,7 +99,7 @@ Import the modules installed.
Create a random secret key that will be used to sign the JWT tokens.
To generate a secure random secret, key use the command:
To generate a secure random secret key use the command:
```bash
openssl rand -hex 32
@@ -104,15 +109,13 @@ And copy the output to the variable `SECRET_KEY` (don't use the one in the examp
Create a variable `ALGORITHM` with the algorithm used to sign the JWT token and set it to `"HS256"`.
And another one for the `TOKEN_SUBJECT`, and set it to, for example, `"access"`.
Create a variable for the expiration of the token.
Define a Pydantic Model that will be used in the token endpoint for the response.
Create a utility function to generate a new access token.
```Python hl_lines="3 6 13 14 15 16 30 31 32 80 81 82 83 84 85 86 87 88"
```Python hl_lines="3 6 13 14 15 29 30 31 79 80 81 82 83 84 85 86 87"
{!./src/security/tutorial004.py!}
```
@@ -124,7 +127,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="91 92 93 94 95 96 97 98 99 100"
```Python hl_lines="90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107"
{!./src/security/tutorial004.py!}
```
@@ -134,10 +137,33 @@ Create a `timedelta` with the expiration time of the token.
Create a real JWT access token and return it.
```Python hl_lines="114 115 116 117 118"
```Python hl_lines="116 117 118 119 120 121 122 123 124 125 126 127 128 129"
{!./src/security/tutorial004.py!}
```
### Technical details about the JWT "subject" `sub`
The JWT specification says that there's a key `sub`, with the subject of the token.
It's optional to use it, but that's where you would put the user's identification, so we are using it here.
JWT might be used for other things apart from identifying a user and allowing him 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.
Using these ideas, JWT can be used for way more sophisticate scenarios.
In those cases, several of those entities could have the same ID, let's say `foo` (a user `foo`, a car `foo`, and a blog post `foo`).
So, to avoid ID collisions, when creating the JWT token for the user, you could prefix the value of the `sub` key, e.g. with `username:`. So, in this example, the value of `sub` could have been: `username:johndoe`.
The important thing to have in mind is that the `sub` key should have a unique identifier across the entire application, and it should be a string.
## Check it
Run the server and go to the docs: <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
@@ -158,7 +184,7 @@ Password: `secret`
<img src="/img/tutorial/security/image08.png">
Call the endpoint `/users/me`, you will get the response as:
Call the endpoint `/users/me/`, you will get the response as:
```JSON
{
@@ -171,7 +197,7 @@ Call the endpoint `/users/me`, you will get the response as:
<img src="/img/tutorial/security/image09.png">
If you open the developer tools, you could see how the data sent and received is just the token, the password is only sent in the first request to authenticate the user:
If you open the developer tools, you could see how the data sent and only includes the token, the password is only sent in the first request to authenticate the user and get that access token, but not afterwards:
<img src="/img/tutorial/security/image10.png">
@@ -180,15 +206,17 @@ If you open the developer tools, you could see how the data sent and received is
## Advanced usage with `scopes`
We didn't use it in this example, but `Security` can receive a parameter `scopes`, as a list of strings.
OAuth2 has the notion of "scopes".
It would describe the scopes required for a specific path operation, as different path operations might require different security scopes, even while using the same `OAuth2PasswordBearer` (or any of the other tools).
You can use them to add a specific set of permissions to a JWT token.
This only applies to OAuth2, and it might be, more or less, an advanced feature, but it is there, if you need to use it.
Then you can give this token to a user directly or a third party, to interact with your API with a set of restrictions.
You can learn how to use them and how they are integrated into **FastAPI** in the next chapter.
## Recap
This concludes our tour for the security features of **FastAPI**.
With what you have seen up to now, you can set up a secure **FastAPI** application using standards like OAuth2 and JWT.
In almost any framework handling the security becomes a rather complex subject quite quickly.
@@ -198,10 +226,14 @@ Many packages that simplify it a lot have to make many compromises with the data
**FastAPI** doesn't make any compromise with any database, data model or tool.
It gives you all the flexibility to chose the ones that fit your project the best.
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.
But it provides you the tools to simplify the process as much as possible without compromising flexibility, robustness or security.
And you can use secure, standard protocols like OAuth2 in a relatively simple way.
And you can use and implement secure, standard protocols, like OAuth2 in a relatively simple way.
In the next (optional) section you can see how to extend this even further, using OAuth2 "scopes", for a more fine-grained permission system, following these same standards.
OAuth2 with scopes (explained in the next section) is the mechanism used by many big authentication providers, like Facebook, Google, GitHub, Microsoft, Twitter, etc.

Some files were not shown because too many files have changed in this diff Show More