Compare commits
66 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
098e629344 | ||
|
|
bbe5f28b77 | ||
|
|
4a0922ebab | ||
|
|
8f16868c6a | ||
|
|
bc3e7f2bbc | ||
|
|
58848be2de | ||
|
|
cfb65d0e15 | ||
|
|
855daa2e53 | ||
|
|
de54e85152 | ||
|
|
b8d3070daf | ||
|
|
471c9cfc2d | ||
|
|
b79c13baed | ||
|
|
332ee4aee1 | ||
|
|
ad40f4a457 | ||
|
|
6b9931f882 | ||
|
|
4c51bb6714 | ||
|
|
57ff677027 | ||
|
|
613c3f3e95 | ||
|
|
bf6d923ca8 | ||
|
|
252188c686 | ||
|
|
510fec9bee | ||
|
|
a73709507c | ||
|
|
75407b9295 | ||
|
|
3180f35bdd | ||
|
|
d498b7feb3 | ||
|
|
3269e6a95c | ||
|
|
f1808de18e | ||
|
|
748dc375db | ||
|
|
b38fb937b0 | ||
|
|
23ef570bf6 | ||
|
|
c25a71e352 | ||
|
|
0c5e684ff9 | ||
|
|
b097a538ab | ||
|
|
a0628f3f6f | ||
|
|
16f6dc66e2 | ||
|
|
07b68365f1 | ||
|
|
0a65c41909 | ||
|
|
d5e782074f | ||
|
|
b5a0c228e5 | ||
|
|
4b94da956c | ||
|
|
b59e8ffcf6 | ||
|
|
7f382127cc | ||
|
|
3513b039b9 | ||
|
|
4cf15503cb | ||
|
|
22dc7e9132 | ||
|
|
7ee32ea197 | ||
|
|
e6ca71523b | ||
|
|
7da9625505 | ||
|
|
ae50492735 | ||
|
|
bcb967f98a | ||
|
|
19c6a2b9bd | ||
|
|
01dd913634 | ||
|
|
343ecb7669 | ||
|
|
e9f2a3c291 | ||
|
|
00e2e544c7 | ||
|
|
dcb076b752 | ||
|
|
659b8ae8af | ||
|
|
2a48b10be3 | ||
|
|
5bd167e0e7 | ||
|
|
58b69493a9 | ||
|
|
786c392bd5 | ||
|
|
acc15230ea | ||
|
|
a84960b13d | ||
|
|
61779949f4 | ||
|
|
10134803a9 | ||
|
|
5085652479 |
2
Pipfile
@@ -20,6 +20,8 @@ mkdocs = "*"
|
||||
mkdocs-material = "*"
|
||||
markdown-include = "*"
|
||||
autoflake = "*"
|
||||
email-validator = "*"
|
||||
ujson = "*"
|
||||
|
||||
[packages]
|
||||
starlette = "*"
|
||||
|
||||
101
Pipfile.lock
generated
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "deb42eb176158abbd62d0af06fc60f571ae7da3012f7baafb1bcca08c363273b"
|
||||
"sha256": "a0f966a95cb84845ca4aad02c44fc0e7c5e2047fc44dcf19a95a4abaa02d0197"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
@@ -34,10 +34,10 @@
|
||||
},
|
||||
"starlette": {
|
||||
"hashes": [
|
||||
"sha256:a7b24496638b9e755bc31a72b0596939f3dd0e6eb1878ff55da5647be026a9e3"
|
||||
"sha256:01f04283b49a8cb0c8921baa90dbafe47e953f0a265f6ebb38176038e4bd9bf8"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.9.8"
|
||||
"version": "==0.9.9"
|
||||
}
|
||||
},
|
||||
"develop": {
|
||||
@@ -174,6 +174,13 @@
|
||||
],
|
||||
"version": "==0.5.0"
|
||||
},
|
||||
"dnspython": {
|
||||
"hashes": [
|
||||
"sha256:36c5e8e38d4369a08b6780b7f27d790a292b2b08eea01607865bf0936c558e01",
|
||||
"sha256:f69c21288a962f4da86e56c4905b49d11aba7938d3d740e80d9e366ee4f1632d"
|
||||
],
|
||||
"version": "==1.16.0"
|
||||
},
|
||||
"docutils": {
|
||||
"hashes": [
|
||||
"sha256:02aec4bd92ab067f6ff27a38a38a41173bf01bed8f89157768c1573f53e474a6",
|
||||
@@ -188,6 +195,13 @@
|
||||
],
|
||||
"version": "==0.1.9"
|
||||
},
|
||||
"email-validator": {
|
||||
"hashes": [
|
||||
"sha256:ddc4b5b59fa699bb10127adcf7ad4de78fde4ec539a072b104b8bb16da666ae5"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.0.3"
|
||||
},
|
||||
"entrypoints": {
|
||||
"hashes": [
|
||||
"sha256:10ad569bb245e7e2ba425285b9fa3e8178a0dc92fc53b1e1c553805e15a8825b",
|
||||
@@ -250,11 +264,11 @@
|
||||
},
|
||||
"jedi": {
|
||||
"hashes": [
|
||||
"sha256:0191c447165f798e6a730285f2eee783fff81b0d3df261945ecb80983b5c3ca7",
|
||||
"sha256:b7493f73a2febe0dc33d51c99b474547f7f6c0b2c8fb2b21f453eef204c12148"
|
||||
"sha256:571702b5bd167911fe9036e5039ba67f820d6502832285cde8c881ab2b2149fd",
|
||||
"sha256:c8481b5e59d34a5c7c42e98f6625e633f6ef59353abea6437472c7ec2093f191"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.13.1"
|
||||
"version": "==0.13.2"
|
||||
},
|
||||
"jinja2": {
|
||||
"hashes": [
|
||||
@@ -457,10 +471,10 @@
|
||||
},
|
||||
"notebook": {
|
||||
"hashes": [
|
||||
"sha256:661341909008d1e7bfa1541904006f9789fa3de1cbec8379d2879819454cc04b",
|
||||
"sha256:91705b109fc785198faed892489cddb233265564d5e2dad5e4f7974af05ee8dd"
|
||||
"sha256:3ab2db8bc10e6edbd264c3c4b800bee276c99818386ee0c146d98d7e6bcf0a67",
|
||||
"sha256:d908673a4010787625c8952e91a22adf737db031f2aa0793ad92f6558918a74a"
|
||||
],
|
||||
"version": "==5.7.2"
|
||||
"version": "==5.7.4"
|
||||
},
|
||||
"pandocfilters": {
|
||||
"hashes": [
|
||||
@@ -564,10 +578,10 @@
|
||||
},
|
||||
"pygments": {
|
||||
"hashes": [
|
||||
"sha256:6301ecb0997a52d2d31385e62d0a4a4cf18d2f2da7054a5ddad5c366cd39cee7",
|
||||
"sha256:82666aac15622bd7bb685a4ee7f6625dd716da3ef7473620c192c0168aae64fc"
|
||||
"sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
|
||||
"sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
|
||||
],
|
||||
"version": "==2.3.0"
|
||||
"version": "==2.3.1"
|
||||
},
|
||||
"pylint": {
|
||||
"hashes": [
|
||||
@@ -610,17 +624,17 @@
|
||||
},
|
||||
"pyrsistent": {
|
||||
"hashes": [
|
||||
"sha256:05910b7ff43cec0a853c15da0bfaf2867faa95f29b08e71f5846a195f1f38c75"
|
||||
"sha256:59880cc33ac293515892b2969aa8f4ed2cec592cbd0be4c4e20f2410468bbc62"
|
||||
],
|
||||
"version": "==0.14.7"
|
||||
"version": "==0.14.8"
|
||||
},
|
||||
"pytest": {
|
||||
"hashes": [
|
||||
"sha256:1d131cc532be0023ef8ae265e2a779938d0619bb6c2510f52987ffcba7fa1ee4",
|
||||
"sha256:ca4761407f1acc85ffd1609f464ca20bb71a767803505bd4127d0e45c5a50e23"
|
||||
"sha256:f689bf2fc18c4585403348dd56f47d87780bf217c53ed9ae7a3e2d7faa45f8e9",
|
||||
"sha256:f812ea39a0153566be53d88f8de94839db1e8a05352ed8a49525d7d7f37861e9"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==4.0.1"
|
||||
"version": "==4.0.2"
|
||||
},
|
||||
"pytest-cov": {
|
||||
"hashes": [
|
||||
@@ -780,32 +794,37 @@
|
||||
},
|
||||
"typed-ast": {
|
||||
"hashes": [
|
||||
"sha256:0948004fa228ae071054f5208840a1e88747a357ec1101c17217bfe99b299d58",
|
||||
"sha256:10703d3cec8dcd9eef5a630a04056bbc898abc19bac5691612acba7d1325b66d",
|
||||
"sha256:1f6c4bd0bdc0f14246fd41262df7dfc018d65bb05f6e16390b7ea26ca454a291",
|
||||
"sha256:25d8feefe27eb0303b73545416b13d108c6067b846b543738a25ff304824ed9a",
|
||||
"sha256:29464a177d56e4e055b5f7b629935af7f49c196be47528cc94e0a7bf83fbc2b9",
|
||||
"sha256:2e214b72168ea0275efd6c884b114ab42e316de3ffa125b267e732ed2abda892",
|
||||
"sha256:3e0d5e48e3a23e9a4d1a9f698e32a542a4a288c871d33ed8df1b092a40f3a0f9",
|
||||
"sha256:519425deca5c2b2bdac49f77b2c5625781abbaf9a809d727d3a5596b30bb4ded",
|
||||
"sha256:57fe287f0cdd9ceaf69e7b71a2e94a24b5d268b35df251a88fef5cc241bf73aa",
|
||||
"sha256:668d0cec391d9aed1c6a388b0d5b97cd22e6073eaa5fbaa6d2946603b4871efe",
|
||||
"sha256:68ba70684990f59497680ff90d18e756a47bf4863c604098f10de9716b2c0bdd",
|
||||
"sha256:6de012d2b166fe7a4cdf505eee3aaa12192f7ba365beeefaca4ec10e31241a85",
|
||||
"sha256:79b91ebe5a28d349b6d0d323023350133e927b4de5b651a8aa2db69c761420c6",
|
||||
"sha256:8550177fa5d4c1f09b5e5f524411c44633c80ec69b24e0e98906dd761941ca46",
|
||||
"sha256:898f818399cafcdb93cbbe15fc83a33d05f18e29fb498ddc09b0214cdfc7cd51",
|
||||
"sha256:94b091dc0f19291adcb279a108f5d38de2430411068b219f41b343c03b28fb1f",
|
||||
"sha256:a26863198902cda15ab4503991e8cf1ca874219e0118cbf07c126bce7c4db129",
|
||||
"sha256:a8034021801bc0440f2e027c354b4eafd95891b573e12ff0418dec385c76785c",
|
||||
"sha256:bc978ac17468fe868ee589c795d06777f75496b1ed576d308002c8a5756fb9ea",
|
||||
"sha256:c05b41bc1deade9f90ddc5d988fe506208019ebba9f2578c622516fd201f5863",
|
||||
"sha256:c9b060bd1e5a26ab6e8267fd46fc9e02b54eb15fffb16d112d4c7b1c12987559",
|
||||
"sha256:edb04bdd45bfd76c8292c4d9654568efaedf76fe78eb246dde69bdb13b2dad87",
|
||||
"sha256:f19f2a4f547505fe9072e15f6f4ae714af51b5a681a97f187971f50c283193b6"
|
||||
"sha256:0555eca1671ebe09eb5f2176723826f6f44cca5060502fea259de9b0e893ab53",
|
||||
"sha256:0ca96128ea66163aea13911c9b4b661cb345eb729a20be15c034271360fc7474",
|
||||
"sha256:16ccd06d614cf81b96de42a37679af12526ea25a208bce3da2d9226f44563868",
|
||||
"sha256:1e21ae7b49a3f744958ffad1737dfbdb43e1137503ccc59f4e32c4ac33b0bd1c",
|
||||
"sha256:37670c6fd857b5eb68aa5d193e14098354783b5138de482afa401cc2644f5a7f",
|
||||
"sha256:46d84c8e3806619ece595aaf4f37743083f9454c9ea68a517f1daa05126daf1d",
|
||||
"sha256:5b972bbb3819ece283a67358103cc6671da3646397b06e7acea558444daf54b2",
|
||||
"sha256:6306ffa64922a7b58ee2e8d6f207813460ca5a90213b4a400c2e730375049246",
|
||||
"sha256:6cb25dc95078931ecbd6cbcc4178d1b8ae8f2b513ae9c3bd0b7f81c2191db4c6",
|
||||
"sha256:7e19d439fee23620dea6468d85bfe529b873dace39b7e5b0c82c7099681f8a22",
|
||||
"sha256:7f5cd83af6b3ca9757e1127d852f497d11c7b09b4716c355acfbebf783d028da",
|
||||
"sha256:81e885a713e06faeef37223a5b1167615db87f947ecc73f815b9d1bbd6b585be",
|
||||
"sha256:94af325c9fe354019a29f9016277c547ad5d8a2d98a02806f27a7436b2da6735",
|
||||
"sha256:b1e5445c6075f509d5764b84ce641a1535748801253b97f3b7ea9d948a22853a",
|
||||
"sha256:cb061a959fec9a514d243831c514b51ccb940b58a5ce572a4e209810f2507dcf",
|
||||
"sha256:cc8d0b703d573cbabe0d51c9d68ab68df42a81409e4ed6af45a04a95484b96a5",
|
||||
"sha256:da0afa955865920edb146926455ec49da20965389982f91e926389666f5cf86a",
|
||||
"sha256:dc76738331d61818ce0b90647aedde17bbba3d3f9e969d83c1d9087b4f978862",
|
||||
"sha256:e7ec9a1445d27dbd0446568035f7106fa899a36f55e52ade28020f7b3845180d",
|
||||
"sha256:f741ba03feb480061ab91a465d1a3ed2d40b52822ada5b4017770dfcb88f839f",
|
||||
"sha256:fe800a58547dd424cd286b7270b967b5b3316b993d86453ede184a17b5a6b17d"
|
||||
],
|
||||
"markers": "python_version < '3.7' and implementation_name == 'cpython'",
|
||||
"version": "==1.1.0"
|
||||
"version": "==1.1.1"
|
||||
},
|
||||
"ujson": {
|
||||
"hashes": [
|
||||
"sha256:f66073e5506e91d204ab0c614a148d5aa938bdbf104751be66f8ad7a222f5f86"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==1.35"
|
||||
},
|
||||
"urllib3": {
|
||||
"hashes": [
|
||||
|
||||
114
README.md
@@ -5,22 +5,22 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/tiangolo/fastapi">
|
||||
<a href="https://travis-ci.org/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.org/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi">
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://codecov.io/gh/tiangolo/fastapi/branch/master/graph/badge.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi">
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**Documentation**: [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com)
|
||||
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**Source Code**: [https://github.com/tiangolo/fastapi](https://github.com/tiangolo/fastapi)
|
||||
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
|
||||
|
||||
---
|
||||
|
||||
@@ -65,6 +65,8 @@ $ pip install uvicorn
|
||||
|
||||
## Example
|
||||
|
||||
### Create it
|
||||
|
||||
* Create a file `main.py` with:
|
||||
|
||||
```Python
|
||||
@@ -72,50 +74,79 @@ from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get('/')
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {'hello': 'world'}
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
<details markdown="1">
|
||||
<summary>Or use <code>async def</code>...</summary>
|
||||
|
||||
Or if your code uses `async` / `await`, use `async def`:
|
||||
If your code uses `async` / `await`, use `async def`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
```Python hl_lines="7 12"
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get('/')
|
||||
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
return {'hello': 'world'}
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
!!! note
|
||||
If you don't know, check the section about [`async` and `await` in the docs](async.md).
|
||||
**Note**:
|
||||
|
||||
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
|
||||
|
||||
</details>
|
||||
|
||||
* Run the server with:
|
||||
### Run it
|
||||
|
||||
Run the server with:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
```
|
||||
|
||||
!!! note
|
||||
The command `uvicorn main:app` refers to:
|
||||
<details markdown="1">
|
||||
<summary>About the command <code>uvicorn main:app --debug</code>...</summary>
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--debug`: make the server restart after code changes. Only do this for development.
|
||||
The command `uvicorn main:app` refers to:
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--debug`: make the server restart after code changes. Only do this for development.
|
||||
|
||||
</details>
|
||||
|
||||
### Check it
|
||||
|
||||
Open your browser at <a href="http://127.0.0.1:8000" target="_blank">http://127.0.0.1:8000</a>.
|
||||
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
|
||||
|
||||
You will see the JSON response as:
|
||||
|
||||
```JSON
|
||||
{"hello": "world"}
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
You already created an API that:
|
||||
|
||||
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
|
||||
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
|
||||
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
|
||||
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
|
||||
|
||||
### Interactive API docs
|
||||
|
||||
Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
@@ -135,14 +166,12 @@ You will see the alternative automatic documentation (provided by <a href="https
|
||||
|
||||
## Example upgrade
|
||||
|
||||
Now modify the file `main.py` to include:
|
||||
Now modify the file `main.py` to recive a body from a `PUT` request.
|
||||
|
||||
* a path parameter `item_id`.
|
||||
* a body, declared using standard Python types (thanks to Pydantic).
|
||||
* an optional query parameter `q`.
|
||||
Declare the body using standard Python types, thanks to Pydantic.
|
||||
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 19"
|
||||
```Python hl_lines="2 7 8 9 10 24"
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -155,14 +184,19 @@ class Item(BaseModel):
|
||||
is_offer: bool = None
|
||||
|
||||
|
||||
@app.get('/')
|
||||
async def read_root():
|
||||
return {'hello': 'world'}
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.post('/items/{item_id}')
|
||||
async def create_item(item_id: int, item: Item, q: str = None):
|
||||
return {"item_name": item.name, "item_id": item_id, "query": q}
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def create_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
The server should reload automatically (because you added `--debug` to the `uvicorn` command above).
|
||||
@@ -171,7 +205,7 @@ The server should reload automatically (because you added `--debug` to the `uvic
|
||||
|
||||
Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
* The interactive API documentation will be automatically updated, including the new query, and body:
|
||||
* The interactive API documentation will be automatically updated, including the new body:
|
||||
|
||||

|
||||
|
||||
@@ -245,13 +279,13 @@ item: Item
|
||||
|
||||
Coming back to the previous code example, **FastAPI** will:
|
||||
|
||||
* Validate that there is an `item_id` in the path.
|
||||
* Validate that the `item_id` is of type `int`.
|
||||
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
|
||||
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
|
||||
* If it is not, the client will see a useful, clear error.
|
||||
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`).
|
||||
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
|
||||
* As the `q` parameter is declared with `= None`, it is optional.
|
||||
* Without the `None` it would be required (as is the body).
|
||||
* Read the body as JSON:
|
||||
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
||||
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
|
||||
* Check that it has a required attribute `name` that should be a `str`.
|
||||
* Check that is has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
||||
@@ -270,7 +304,7 @@ We just scratched the surface, but you already get the idea of how it all works.
|
||||
Try changing the line with:
|
||||
|
||||
```Python
|
||||
return {"item_name": item.name, "item_id": item_id, "query": q}
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
...from:
|
||||
@@ -290,7 +324,7 @@ Try changing the line with:
|
||||

|
||||
|
||||
|
||||
For a more complete example including more features, [see the tutorial - user guide](tutorial/intro/).
|
||||
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/intro/">Tutorial - User Guide</a>.
|
||||
|
||||
**Spoiler alert**: the tutorial - user guide includes:
|
||||
|
||||
|
||||
15
dist/fastapi-0.1.0/pyproject.toml
vendored
@@ -1,15 +0,0 @@
|
||||
[build-system]
|
||||
requires = ["flit"]
|
||||
build-backend = "flit.buildapi"
|
||||
|
||||
[tool.flit.metadata]
|
||||
module = "fastapi"
|
||||
author = "Sebastián Ramírez"
|
||||
author-email = "tiangolo@gmail.com"
|
||||
home-page = "https://github.com/tiangolo/fastapi"
|
||||
classifiers = ["License :: OSI Approved :: MIT License"]
|
||||
requires = [
|
||||
"starlette >=0.9.7",
|
||||
"pydantic >=0.16"
|
||||
]
|
||||
requires-python = ">=3.6"
|
||||
@@ -27,7 +27,7 @@ Interactive API documentation and exploration web user interfaces. As the framew
|
||||
|
||||
It's all based on standard **Python 3.6 type** declarations (thanks to Pydantic). No new syntax to learn. Just standard modern Python.
|
||||
|
||||
If you need a 2 minute refresher of how to use Python types (even if you don't use FastAPI), check the tutorial section: [Python types](tutorial/python-types.md).
|
||||
If you need a 2 minute refresher of how to use Python types (even if you don't use FastAPI), check the tutorial section: [Python types](python-types.md).
|
||||
|
||||
You write standard Python with types:
|
||||
|
||||
@@ -61,15 +61,14 @@ second_user_data = {
|
||||
"joined": "2018-11-30",
|
||||
}
|
||||
|
||||
|
||||
# **second_user_data means:
|
||||
# pass the keys and values of the dict
|
||||
# directly as key-value arguments
|
||||
# equivalent to:
|
||||
# id=4, name="Mary", joined="2018-11-30"
|
||||
my_second_user: User = User(**second_user_data)
|
||||
```
|
||||
|
||||
!!! info
|
||||
`**second_user_data` means:
|
||||
|
||||
Pass the keys and values of the `second_user_data` dict directly as key-value arguments, equivalent to: `User(id=4, name="Mary", joined="2018-11-30")`
|
||||
|
||||
### Editor support
|
||||
|
||||
All the framework was designed to be easy and intuitive to use, all the decisons where tested on multiple editors even before starting development, to ensure the best development experience.
|
||||
|
||||
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 72 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 67 KiB |
|
Before Width: | Height: | Size: 66 KiB After Width: | Height: | Size: 73 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 66 KiB |
|
Before Width: | Height: | Size: 88 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 76 KiB After Width: | Height: | Size: 77 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 50 KiB |
BIN
docs/img/python-types/image01.png
Normal file
|
After Width: | Height: | Size: 17 KiB |
BIN
docs/img/python-types/image02.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
docs/img/python-types/image03.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/img/python-types/image04.png
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
docs/img/python-types/image05.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
docs/img/python-types/image06.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 65 KiB |
BIN
docs/img/tutorial/security/image01.png
Normal file
|
After Width: | Height: | Size: 53 KiB |
BIN
docs/img/tutorial/security/image02.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/img/tutorial/security/image03.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
docs/img/tutorial/security/image04.png
Normal file
|
After Width: | Height: | Size: 82 KiB |
BIN
docs/img/tutorial/security/image05.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
docs/img/tutorial/security/image06.png
Normal file
|
After Width: | Height: | Size: 89 KiB |
BIN
docs/img/tutorial/security/image07.png
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
docs/img/tutorial/security/image08.png
Normal file
|
After Width: | Height: | Size: 86 KiB |
BIN
docs/img/tutorial/security/image09.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
BIN
docs/img/tutorial/security/image10.png
Normal file
|
After Width: | Height: | Size: 155 KiB |
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 61 KiB |
116
docs/index.md
@@ -5,22 +5,22 @@
|
||||
<em>FastAPI framework, high performance, easy to learn, fast to code, ready for production</em>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://travis-ci.org/tiangolo/fastapi">
|
||||
<a href="https://travis-ci.org/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://travis-ci.org/tiangolo/fastapi.svg?branch=master" alt="Build Status">
|
||||
</a>
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi">
|
||||
<a href="https://codecov.io/gh/tiangolo/fastapi" target="_blank">
|
||||
<img src="https://codecov.io/gh/tiangolo/fastapi/branch/master/graph/badge.svg" alt="Coverage">
|
||||
</a>
|
||||
<a href="https://pypi.org/project/fastapi">
|
||||
<a href="https://pypi.org/project/fastapi" target="_blank">
|
||||
<img src="https://badge.fury.io/py/fastapi.svg" alt="Package version">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
**Documentation**: [https://fastapi.tiangolo.com](https://fastapi.tiangolo.com)
|
||||
**Documentation**: <a href="https://fastapi.tiangolo.com" target="_blank">https://fastapi.tiangolo.com</a>
|
||||
|
||||
**Source Code**: [https://github.com/tiangolo/fastapi](https://github.com/tiangolo/fastapi)
|
||||
**Source Code**: <a href="https://github.com/tiangolo/fastapi" target="_blank">https://github.com/tiangolo/fastapi</a>
|
||||
|
||||
---
|
||||
|
||||
@@ -36,7 +36,7 @@ The key features are:
|
||||
* **Easy**: Designed to be easy to use and learn. Less time reading docs.
|
||||
* **Short**: Minimize code duplication. Multiple features from each parameter declaration. Less bugs.
|
||||
* **Robust**: Get production-ready code. With automatic interactive documentation.
|
||||
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" target="_blank">OpenAPI</a> and <a href="http://json-schema.org/" target="_blank">JSON Schema</a>.
|
||||
* **Standards-based**: Based on (and fully compatible with) the open standards for APIs: <a href="https://github.com/OAI/OpenAPI-Specification" target="_blank">OpenAPI</a> (previously known as Swagger) and <a href="http://json-schema.org/" target="_blank">JSON Schema</a>.
|
||||
|
||||
<small>* estimation based on tests on an internal development team, building production applications.</small>
|
||||
|
||||
@@ -65,6 +65,8 @@ $ pip install uvicorn
|
||||
|
||||
## Example
|
||||
|
||||
### Create it
|
||||
|
||||
* Create a file `main.py` with:
|
||||
|
||||
```Python
|
||||
@@ -72,50 +74,79 @@ from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get('/')
|
||||
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {'hello': 'world'}
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
<details markdown="1">
|
||||
<summary>Or use <code>async def</code>...</summary>
|
||||
|
||||
Or if your code uses `async` / `await`, use `async def`:
|
||||
If your code uses `async` / `await`, use `async def`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
```Python hl_lines="7 12"
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@app.get('/')
|
||||
|
||||
@app.get("/")
|
||||
async def read_root():
|
||||
return {'hello': 'world'}
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.get("/items/{item_id}")
|
||||
async def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
```
|
||||
|
||||
!!! note
|
||||
If you don't know, check the section about [`async` and `await` in the docs](async.md).
|
||||
**Note**:
|
||||
|
||||
If you don't know, check the _"In a hurry?"_ section about <a href="https://fastapi.tiangolo.com/async/#in-a-hurry" target="_blank">`async` and `await` in the docs</a>.
|
||||
|
||||
</details>
|
||||
|
||||
* Run the server with:
|
||||
### Run it
|
||||
|
||||
Run the server with:
|
||||
|
||||
```bash
|
||||
uvicorn main:app --debug
|
||||
```
|
||||
|
||||
!!! note
|
||||
The command `uvicorn main:app` refers to:
|
||||
<details markdown="1">
|
||||
<summary>About the command <code>uvicorn main:app --debug</code>...</summary>
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--debug`: make the server restart after code changes. Only do this for development.
|
||||
The command `uvicorn main:app` refers to:
|
||||
|
||||
* `main`: the file `main.py` (the Python "module").
|
||||
* `app`: the object created inside of `main.py` with the line `app = FastAPI()`.
|
||||
* `--debug`: make the server restart after code changes. Only do this for development.
|
||||
|
||||
</details>
|
||||
|
||||
### Check it
|
||||
|
||||
Open your browser at <a href="http://127.0.0.1:8000" target="_blank">http://127.0.0.1:8000</a>.
|
||||
Open your browser at <a href="http://127.0.0.1:8000/items/5?q=somequery" target="_blank">http://127.0.0.1:8000/items/5?q=somequery</a>.
|
||||
|
||||
You will see the JSON response as:
|
||||
|
||||
```JSON
|
||||
{"hello": "world"}
|
||||
{"item_id": 5, "q": "somequery"}
|
||||
```
|
||||
|
||||
You already created an API that:
|
||||
|
||||
* Receives HTTP requests in the _paths_ `/` and `/items/{item_id}`.
|
||||
* Both _paths_ take `GET` <em>operations</em> (also known as HTTP _methods_).
|
||||
* The _path_ `/items/{item_id}` has a _path parameter_ `item_id` that should be an `int`.
|
||||
* The _path_ `/items/{item_id}` has an optional `str` _query parameter_ `q`.
|
||||
|
||||
### Interactive API docs
|
||||
|
||||
Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
@@ -135,14 +166,12 @@ You will see the alternative automatic documentation (provided by <a href="https
|
||||
|
||||
## Example upgrade
|
||||
|
||||
Now modify the file `main.py` to include:
|
||||
Now modify the file `main.py` to recive a body from a `PUT` request.
|
||||
|
||||
* a path parameter `item_id`.
|
||||
* a body, declared using standard Python types (thanks to Pydantic).
|
||||
* an optional query parameter `q`.
|
||||
Declare the body using standard Python types, thanks to Pydantic.
|
||||
|
||||
|
||||
```Python hl_lines="2 7 8 9 10 19"
|
||||
```Python hl_lines="2 7 8 9 10 24"
|
||||
from fastapi import FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -155,14 +184,19 @@ class Item(BaseModel):
|
||||
is_offer: bool = None
|
||||
|
||||
|
||||
@app.get('/')
|
||||
async def read_root():
|
||||
return {'hello': 'world'}
|
||||
@app.get("/")
|
||||
def read_root():
|
||||
return {"Hello": "World"}
|
||||
|
||||
|
||||
@app.post('/items/{item_id}')
|
||||
async def create_item(item_id: int, item: Item, q: str = None):
|
||||
return {"item_name": item.name, "item_id": item_id, "query": q}
|
||||
@app.get("/items/{item_id}")
|
||||
def read_item(item_id: int, q: str = None):
|
||||
return {"item_id": item_id, "q": q}
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
def create_item(item_id: int, item: Item):
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
The server should reload automatically (because you added `--debug` to the `uvicorn` command above).
|
||||
@@ -171,7 +205,7 @@ The server should reload automatically (because you added `--debug` to the `uvic
|
||||
|
||||
Now go to <a href="http://127.0.0.1:8000/docs" target="_blank">http://127.0.0.1:8000/docs</a>.
|
||||
|
||||
* The interactive API documentation will be automatically updated, including the new query, and body:
|
||||
* The interactive API documentation will be automatically updated, including the new body:
|
||||
|
||||

|
||||
|
||||
@@ -245,13 +279,13 @@ item: Item
|
||||
|
||||
Coming back to the previous code example, **FastAPI** will:
|
||||
|
||||
* Validate that there is an `item_id` in the path.
|
||||
* Validate that the `item_id` is of type `int`.
|
||||
* Validate that there is an `item_id` in the path for `GET` and `PUT` requests.
|
||||
* Validate that the `item_id` is of type `int` for `GET` and `PUT` requests.
|
||||
* If it is not, the client will see a useful, clear error.
|
||||
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`).
|
||||
* Check if there is an optional query parameter named `q` (as in `http://127.0.0.1:8000/items/foo?q=somequery`) for `GET` requests.
|
||||
* As the `q` parameter is declared with `= None`, it is optional.
|
||||
* Without the `None` it would be required (as is the body).
|
||||
* Read the body as JSON:
|
||||
* Without the `None` it would be required (as is the body in the case with `PUT`).
|
||||
* For `PUT` requests to `/items/{item_id}`, Read the body as JSON:
|
||||
* Check that it has a required attribute `name` that should be a `str`.
|
||||
* Check that is has a required attribute `price` that has to be a `float`.
|
||||
* Check that it has an optional attribute `is_offer`, that should be a `bool`, if present.
|
||||
@@ -270,7 +304,7 @@ We just scratched the surface, but you already get the idea of how it all works.
|
||||
Try changing the line with:
|
||||
|
||||
```Python
|
||||
return {"item_name": item.name, "item_id": item_id, "query": q}
|
||||
return {"item_name": item.name, "item_id": item_id}
|
||||
```
|
||||
|
||||
...from:
|
||||
@@ -290,7 +324,7 @@ Try changing the line with:
|
||||

|
||||
|
||||
|
||||
For a more complete example including more features, [see the tutorial - user guide](tutorial/intro/).
|
||||
For a more complete example including more features, see the <a href="https://fastapi.tiangolo.com/tutorial/intro/">Tutorial - User Guide</a>.
|
||||
|
||||
**Spoiler alert**: the tutorial - user guide includes:
|
||||
|
||||
|
||||
42
docs/project-generation.md
Normal file
@@ -0,0 +1,42 @@
|
||||
There is a project generator that you can use to get started, with a lot of the initial set up, security, database and first API endpoints already done for you.
|
||||
|
||||
## Full-Stack-FastAPI-Couchbase
|
||||
|
||||
GitHub: <a href="https://github.com/tiangolo/full-stack-fastapi-couchbase" target="_blank">https://github.com/tiangolo/full-stack-fastapi-couchbase</a>
|
||||
|
||||
### Features
|
||||
|
||||
* Full **Docker** integration (Docker based).
|
||||
* Docker Swarm Mode deployment.
|
||||
* **Docker Compose** integration and optimization for local development.
|
||||
* **Production ready** Python web server using Uvicorn and Gunicorn.
|
||||
* Python **FastAPI** backend with all its features.
|
||||
* **Celery** worker that can import and use code from the rest of the backend selectively (you don't have to install the complete app in each worker).
|
||||
* **NoSQL Couchbase** database that supports direct synchronization via Couchbase Sync Gateway for offline-first applications.
|
||||
* **Full Text Search** integrated, using Couchbase.
|
||||
* REST backend tests based on Pytest, integrated with Docker, so you can test the full API interaction, independent on the database. As it runs in Docker, it can build a new data store from scratch each time (so you can use ElasticSearch, MongoDB, or whatever you want, and just test that the API works).
|
||||
* Easy Python integration with **Jupyter** Kernels for remote or in-Docker development with extensions like Atom Hydrogen or Visual Studio Code Jupyter.
|
||||
* **Email notifications** for account creation and password recovery, compatible with:
|
||||
* Mailgun
|
||||
* SparkPost
|
||||
* SendGrid
|
||||
* ...any other provider that can generate standard SMTP credentials.
|
||||
* **Vue** frontend:
|
||||
* Generated with Vue CLI.
|
||||
* **JWT Authentication** handling.
|
||||
* Login view.
|
||||
* After login, main dashboard view.
|
||||
* Main dashboard with user creation and edition.
|
||||
* Self user edition.
|
||||
* **Vuex**.
|
||||
* **Vue-router**.
|
||||
* **Vuetify** for beautiful material design components.
|
||||
* **TypeScript**.
|
||||
* Docker server based on **Nginx** (configured to play nicely with Vue-router).
|
||||
* Docker multi-stage building, so you don't need to save or commit compiled code.
|
||||
* Frontend tests ran at build time (can be disabled too).
|
||||
* Made as modular as possible, so it works out of the box, but you can re-generate with Vue CLI or create it as you need, and re-use what you want.
|
||||
* Flower for Celery jobs monitoring.
|
||||
* Load balancing between frontend and backend with **Traefik**, so you can have both under the same domain, separated by path, but served by different containers.
|
||||
* Traefik integration, including Let's Encrypt **HTTPS** certificates automatic generation.
|
||||
* GitLab **CI** (continuous integration), including frontend and backend testing.
|
||||
288
docs/python-types.md
Normal file
@@ -0,0 +1,288 @@
|
||||
**Python 3.6+** has support for optional "type hints".
|
||||
|
||||
These **"type hints"** are a new syntax (since Python 3.6+) that allow declaring the <abbr title="for example: str, int, float, bool">type</abbr> of a variable.
|
||||
|
||||
By declaring types for your variables, editors and tools can give you better support.
|
||||
|
||||
This is just a **quick tutorial / refresher** about Python type hints. It covers only the minimum necessary to use them with **FastAPI**... which is actually very little.
|
||||
|
||||
**FastAPI** is all based on these type hints, they give it many advantages and benefits.
|
||||
|
||||
But even if you never use **FastAPI**, you would benefit from learning a bit about them.
|
||||
|
||||
!!! note
|
||||
If you are a Python expert, and you already know everything about type hints, skip to the next chapter.
|
||||
|
||||
## Motivation
|
||||
|
||||
Let's start with a simple example:
|
||||
|
||||
```Python
|
||||
{!./src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
Calling this program outputs:
|
||||
|
||||
```
|
||||
John Doe
|
||||
```
|
||||
|
||||
The function does the following:
|
||||
|
||||
* Takes a `fist_name` and `last_name`.
|
||||
* Converts the first letter of each one to upper case with `title()`.
|
||||
* <abbr title="Puts them together, as one. With the contents of one after the other.">Concatenates</abbr> them with a space in the middle.
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./src/python_types/tutorial001.py!}
|
||||
```
|
||||
|
||||
### Edit it
|
||||
|
||||
It's a very simple program.
|
||||
|
||||
But now imagine that you were writing it from scratch.
|
||||
|
||||
At some point you would have started the definition of the function, you had the parameters ready...
|
||||
|
||||
But then you have to call "that method that converts the first letter to upper case".
|
||||
|
||||
Was it `upper`? Was it `uppercase`? `first_uppercase`? `capitalize`?
|
||||
|
||||
Then, you try with the old programer's friend, editor autocompletion.
|
||||
|
||||
You type the first parameter of the function, `first_name`, then a dot (`.`) and then hit `Ctrl+Space` to trigger the completion.
|
||||
|
||||
But, sadly, you get nothing useful:
|
||||
|
||||
<img src="/img/python-types/image01.png">
|
||||
|
||||
### Add types
|
||||
|
||||
Let's modify a single line from the previous version.
|
||||
|
||||
We will change exactly this fragment, the parameters of the function, from:
|
||||
|
||||
```Python
|
||||
first_name, last_name
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```Python
|
||||
first_name: str, last_name: str
|
||||
```
|
||||
|
||||
That's it.
|
||||
|
||||
Those are the "type hints":
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./src/python_types/tutorial002.py!}
|
||||
```
|
||||
|
||||
That is not the same as declaring default values like would be with:
|
||||
|
||||
```Python
|
||||
first_name="john", last_name="doe"
|
||||
```
|
||||
|
||||
It's a different thing.
|
||||
|
||||
We are using colons (`:`), not equals (`=`).
|
||||
|
||||
And adding type hints normally doesn't change what happens from what would happen without them.
|
||||
|
||||
But now, imagine you are again in the middle of creating that function, but with type hints.
|
||||
|
||||
At the same point, you try to trigger the autocomplete with `Ctrl+Space` and you see:
|
||||
|
||||
<img src="/img/python-types/image02.png">
|
||||
|
||||
With that, you can scroll, seeing the options, until you find the one that "rings a bell":
|
||||
|
||||
<img src="/img/python-types/image03.png">
|
||||
|
||||
## More motivation
|
||||
|
||||
Check this function, it already has type hints:
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./src/python_types/tutorial003.py!}
|
||||
```
|
||||
|
||||
Because the editor knows the types of the variables, you don't only get completion, you also get error checks:
|
||||
|
||||
<img src="/img/python-types/image04.png">
|
||||
|
||||
Now you know that you have to fix it, convert `age` to a string with `str(age)`:
|
||||
|
||||
```Python hl_lines="2"
|
||||
{!./src/python_types/tutorial004.py!}
|
||||
```
|
||||
|
||||
|
||||
## Declaring types
|
||||
|
||||
You just saw the main place to declare type hints. As function parameters.
|
||||
|
||||
This is also the main place you would use them with **FastAPI**.
|
||||
|
||||
### Simple types
|
||||
|
||||
You can declare all the standard Python types, not only `str`.
|
||||
|
||||
You can use, for example:
|
||||
|
||||
* `int`
|
||||
* `float`
|
||||
* `bool`
|
||||
* `bytes`
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./src/python_types/tutorial005.py!}
|
||||
```
|
||||
|
||||
### Types with subtypes
|
||||
|
||||
There are some data structures that can contain other values, like `dict`, `list`, `set` and `tuple`. And the internal values can have their own type too.
|
||||
|
||||
To declare those types and the subtypes, you can use the standard Python module `typing`.
|
||||
|
||||
It exists specifically to support these type hints.
|
||||
|
||||
#### Lists
|
||||
|
||||
For example, let's define a variable to be a `list` of `str`.
|
||||
|
||||
From `typing`, import `List` (with a capital `L`):
|
||||
|
||||
```Python hl_lines="1"
|
||||
{!./src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
Declare the variable, with the same colon (`:`) syntax.
|
||||
|
||||
As the type, put the `List`.
|
||||
|
||||
As the list is a type that takes a "subtype", you put the subtype in square brackets:
|
||||
|
||||
```Python hl_lines="4"
|
||||
{!./src/python_types/tutorial006.py!}
|
||||
```
|
||||
|
||||
That means: "the variable `items` is a `list`, and each of the items in this list is a `str`".
|
||||
|
||||
By doing that, your editor can provide support even while processing items from the list.
|
||||
|
||||
Without types, that's almost impossible to achieve:
|
||||
|
||||
<img src="/img/python-types/image05.png">
|
||||
|
||||
Notice that the variable `item` is one of the elements in the list `items`.
|
||||
|
||||
And still, the editor knows it is a `str`, and provides support for that.
|
||||
|
||||
#### Tuples and Sets
|
||||
|
||||
You would do the same to declare `tuple`s and `set`s:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!./src/python_types/tutorial007.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
* The variable `items_t` is a `tuple`, and each of its items is an `int`.
|
||||
* The variable `items_s` is a `set`, and each of its items is of type `bytes`.
|
||||
|
||||
#### Dicts
|
||||
|
||||
To define a `dict`, you pass 2 subtypes, separated by commas.
|
||||
|
||||
The first subtype is for the keys of the `dict`.
|
||||
|
||||
The second subtype is for the values of the `dict`:
|
||||
|
||||
```Python hl_lines="1 4"
|
||||
{!./src/python_types/tutorial008.py!}
|
||||
```
|
||||
|
||||
This means:
|
||||
|
||||
* The variable `prices` is a `dict`:
|
||||
* The keys of this `dict` are of type `str` (let's say, the name of each item).
|
||||
* The values of this `dict` are of type `float` (let's say, the price of each item).
|
||||
|
||||
|
||||
### Classes as types
|
||||
|
||||
You can also declare a class as the type of a variable.
|
||||
|
||||
Let's say you have a class `Person`, with a name:
|
||||
|
||||
```Python hl_lines="1 2 3"
|
||||
{!./src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
Then you can declare a variable to be of type `Person`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/python_types/tutorial009.py!}
|
||||
```
|
||||
|
||||
And then, again, you get all the editor support:
|
||||
|
||||
<img src="/img/python-types/image06.png">
|
||||
|
||||
|
||||
## Pydantic models
|
||||
|
||||
<a href="https://pydantic-docs.helpmanual.io/" target="_blank">Pydantic</a> is a Python library to perform data validation.
|
||||
|
||||
You declare the "shape" of the data as classes with attributes.
|
||||
|
||||
And each attribute has a type.
|
||||
|
||||
Then you create an instance of that class with some values and it will validate the values, convert them to the appropriate type (if that's the case) and give you an object with all the data.
|
||||
|
||||
And you get all the editor support with that resulting object.
|
||||
|
||||
Taken from the official Pydantic docs:
|
||||
|
||||
```Python
|
||||
{!./src/python_types/tutorial010.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
To learn more about <a href="https://pydantic-docs.helpmanual.io/" target="_blank">Pydantic, check its docs</a>.
|
||||
|
||||
**FastAPI** is all based on Pydantic.
|
||||
|
||||
You will see a lot more of all this in practice in the <a href="/tutorial/intro/" target="_blank">Tutorial - User Guide</a> (the next section).
|
||||
|
||||
|
||||
## Type hints in **FastAPI**
|
||||
|
||||
**FastAPI** takes advantage of these type hints to do several things.
|
||||
|
||||
With **FastAPI** you declare parameters with type hints and you get:
|
||||
|
||||
* **Editor support**.
|
||||
* **Type checks**.
|
||||
|
||||
...and **FastAPI** uses the same declarations to:
|
||||
|
||||
* **Define requirements**: from request path parameters, query parameters, headers, bodies, dependencies, etc.
|
||||
* **Convert data**: from the request to the required type.
|
||||
* **Validate data**: coming from each request:
|
||||
* Generating **automatic errors** returned to the client when the data is invalid.
|
||||
* **Document** the API using OpenAPI:
|
||||
* which is then used by the automatic interactive documentation user interfaces.
|
||||
|
||||
This might all sound abstract. Don't worry. You'll see all this in action in the <a href="/tutorial/intro/" target="_blank">Tutorial - User Guide</a> (the next section).
|
||||
|
||||
The important thing is that by using standard Python types, in a single place (instead of adding more classes, decorators, etc), **FastAPI** will do a lot of the work for you.
|
||||
|
||||
!!! info
|
||||
If you already went through all the tutorial and came back to see more about types, a good resource is <a href="https://mypy.readthedocs.io/en/latest/cheat_sheet_py3.html" target="_blank">the "cheat sheet" from `mypy`</a>.
|
||||
0
docs/src/bigger_applications/app/__init__.py
Normal file
@@ -8,11 +8,11 @@ async def read_users():
|
||||
return [{"username": "Foo"}, {"username": "Bar"}]
|
||||
|
||||
|
||||
@router.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"username": username}
|
||||
|
||||
|
||||
@router.get("/users/me")
|
||||
async def read_user_me():
|
||||
return {"username": "fakecurrentuser"}
|
||||
|
||||
|
||||
@router.get("/users/{username}")
|
||||
async def read_user(username: str):
|
||||
return {"username": username}
|
||||
@@ -1,7 +1,7 @@
|
||||
from fastapi import FastAPI
|
||||
|
||||
from .tutorial01 import router as users_router
|
||||
from .tutorial02 import router as items_router
|
||||
from .routers.tutorial001 import router as users_router
|
||||
from .routers.tutorial002 import router as items_router
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -15,7 +15,7 @@ class Item(BaseModel):
|
||||
async def update_item(
|
||||
*,
|
||||
item_id: int = Path(..., title="The ID of the item to get", ge=0, le=1000),
|
||||
q: str,
|
||||
q: str = None,
|
||||
item: Item = None,
|
||||
):
|
||||
results = {"item_id": item_id}
|
||||
@@ -1,5 +1,4 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
@@ -7,18 +6,15 @@ app = FastAPI()
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
class CommonQueryParams(BaseModel):
|
||||
q: str = None
|
||||
skip: int = None
|
||||
limit: int = None
|
||||
|
||||
|
||||
async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
|
||||
return CommonQueryParams(q=q, skip=skip, limit=limit)
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
|
||||
self.q = q
|
||||
self.skip = skip
|
||||
self.limit = limit
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(commons: CommonQueryParams = Depends(common_parameters)):
|
||||
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
23
docs/src/dependencies/tutorial003.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
|
||||
self.q = q
|
||||
self.skip = skip
|
||||
self.limit = limit
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(commons=Depends(CommonQueryParams)):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
items = fake_items_db[commons.skip : commons.limit]
|
||||
response.update({"items": items})
|
||||
return response
|
||||
23
docs/src/dependencies/tutorial004.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
|
||||
self.q = q
|
||||
self.skip = skip
|
||||
self.limit = limit
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(commons: CommonQueryParams = Depends()):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
items = fake_items_db[commons.skip : commons.limit]
|
||||
response.update({"items": items})
|
||||
return response
|
||||
20
docs/src/dependencies/tutorial005.py
Normal file
@@ -0,0 +1,20 @@
|
||||
from fastapi import Cookie, Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def query_extractor(q: str = None):
|
||||
return q
|
||||
|
||||
|
||||
def query_or_cookie_extractor(
|
||||
q: str = Depends(query_extractor), last_query: str = Cookie(None)
|
||||
):
|
||||
if not q:
|
||||
return last_query
|
||||
return q
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
|
||||
return {"q_or_cookie": query_or_default}
|
||||
21
docs/src/dependencies/tutorial006.py
Normal 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}
|
||||
27
docs/src/extra_data_types/tutorial001.py
Normal file
@@ -0,0 +1,27 @@
|
||||
from datetime import datetime, time, timedelta
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import Body, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
@app.put("/items/{item_id}")
|
||||
async def read_items(
|
||||
item_id: UUID,
|
||||
start_datetime: datetime = Body(None),
|
||||
end_datetime: datetime = Body(None),
|
||||
repeat_at: time = Body(None),
|
||||
process_after: timedelta = Body(None),
|
||||
):
|
||||
start_process = start_datetime + process_after
|
||||
duration = end_datetime - start_process
|
||||
return {
|
||||
"item_id": item_id,
|
||||
"start_datetime": start_datetime,
|
||||
"end_datetime": end_datetime,
|
||||
"repeat_at": repeat_at,
|
||||
"process_after": process_after,
|
||||
"start_process": start_process,
|
||||
"duration": duration,
|
||||
}
|
||||
@@ -11,10 +11,14 @@ USERPROFILE_DOC_TYPE = "userprofile"
|
||||
|
||||
|
||||
def get_bucket():
|
||||
cluster = Cluster("couchbase://couchbasehost:8091")
|
||||
cluster = Cluster(
|
||||
"couchbase://couchbasehost:8091?fetch_mutation_tokens=1&operation_timeout=30&n1ql_timeout=300"
|
||||
)
|
||||
authenticator = PasswordAuthenticator("username", "password")
|
||||
cluster.authenticate(authenticator)
|
||||
bucket: Bucket = cluster.open_bucket("bucket_name", lockmode=LOCKMODE_WAIT)
|
||||
bucket.timeout = 30
|
||||
bucket.n1ql_timeout = 300
|
||||
return bucket
|
||||
|
||||
|
||||
@@ -29,9 +33,6 @@ class UserInDB(User):
|
||||
type: str = USERPROFILE_DOC_TYPE
|
||||
hashed_password: str
|
||||
|
||||
class Meta:
|
||||
key: Optional[str] = None
|
||||
|
||||
|
||||
def get_user(bucket: Bucket, username: str):
|
||||
doc_id = f"userprofile::{username}"
|
||||
@@ -39,7 +40,6 @@ def get_user(bucket: Bucket, username: str):
|
||||
if not result.value:
|
||||
return None
|
||||
user = UserInDB(**result.value)
|
||||
user.Meta.key = result.key
|
||||
return user
|
||||
|
||||
|
||||
6
docs/src/python_types/tutorial001.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def get_full_name(first_name, last_name):
|
||||
full_name = first_name.title() + " " + last_name.title()
|
||||
return full_name
|
||||
|
||||
|
||||
print(get_full_name("john", "doe"))
|
||||
6
docs/src/python_types/tutorial002.py
Normal file
@@ -0,0 +1,6 @@
|
||||
def get_full_name(first_name: str, last_name: str):
|
||||
full_name = first_name.title() + " " + last_name.title()
|
||||
return full_name
|
||||
|
||||
|
||||
print(get_full_name("john", "doe"))
|
||||