You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
2.[Excessively use Pydantic for data validation.](https://github.com/zhanymkanov/fastapi-best-practices#2-excessively-use-pydantic-for-data-validation)
23.[If you must use sync SDK, then run it in a thread pool.](https://github.com/zhanymkanov/fastapi-best-practices#23-if-you-must-use-sync-sdk-then-run-it-in-a-thread-pool)
<pstyle="text-align: center;"> <i>Project <ahref="https://github.com/zhanymkanov/fastapi_production_template">sample</a> built with these best-practices in mind. </i> </p>
pong =await service.async_get_pong() # non-blocking I/O db call
372
370
373
371
return {"pong": pong}
374
372
@@ -390,6 +388,7 @@ async def perfect_ping():
390
388
3. While `good_ping` is being executed, event loop selects next tasks from the queue and works on them (e.g. accept new request, call db)
391
389
- Independently of main thread (i.e. our FastAPI app),
392
390
worker thread will be waiting for `time.sleep` to finish and then for `service.get_pong` to finish
391
+
- Sync operation blocks only the side thread, not the main one.
393
392
4. When `good_ping` finishes its work, server returns a response to the client
394
393
3.`GET /perfect-ping`
395
394
1. FastAPI server receives a request and starts handling it
@@ -399,7 +398,7 @@ async def perfect_ping():
399
398
5. Event loop selects next tasks from the queue and works on them (e.g. accept new request, call db)
400
399
6. When `service.async_get_pong` is done, server returns a response to the client
401
400
402
-
The second caveat is that operations that are non-blocking awaitables or are sent to thread pool must be I/O intensive tasks (e.g. open file, db call, external API call).
401
+
The second caveat is that operations that are non-blocking awaitables or are sent to the thread pool must be I/O intensive tasks (e.g. open file, db call, external API call).
403
402
- Awaiting CPU-intensive tasks (e.g. heavy calculations, data processing, video transcoding) is worthless since the CPU has to work to finish the tasks,
404
403
while I/O operations are external and server does nothing while waiting for that operations to finish, thus it can go to the next tasks.
405
404
- Running CPU-intensive tasks in other threads also isn't effective, because of [GIL](https://realpython.com/python-gil/).
Unless you have sync db connections (excuse me?) or aren't planning to write integration tests.
597
596
### 15. BackgroundTasks > asyncio.create_task
598
597
BackgroundTasks can [effectively run](https://github.com/encode/starlette/blob/31164e346b9bd1ce17d968e1301c3bb2c23bb418/starlette/background.py#L25)
599
-
both blocking and non-blocking I/O operations the same way it handles routes (`sync`functions are run in a threadpool, while `async`ones are awaited later)
598
+
both blocking and non-blocking I/O operations the same way FastAPI handles blocking routes (`sync`tasks are run in a threadpool, while `async`tasks are awaited later)
600
599
- Don't lie to the worker and don't mark blocking I/O operations as `async`
601
600
- Don't use it for heavy CPU intensive tasks.
602
601
```python
@@ -666,9 +665,9 @@ print(type(post.content))
666
665
# Article is very inclusive and all fields are optional, allowing any dict to become valid
667
666
```
668
667
**Solutions:**
669
-
1. Validate input has only valid fields
668
+
1. Validate input has only allowed valid fields and raise error if unknowns are provided
670
669
```python
671
-
from pydantic import BaseModel, Extra, root_validator
670
+
from pydantic import BaseModel, Extra
672
671
673
672
classArticle(BaseModel):
674
673
text: str|None
@@ -733,7 +732,7 @@ print(type(p.content))
733
732
# OUTPUT: Article, because smart_union doesn't work for complex fields like classes
734
733
```
735
734
736
-
**Fast Workaround:**
735
+
3.Fast Workaround
737
736
738
737
Order field types properly: from the most strict ones to loose ones.
739
738
@@ -950,7 +949,7 @@ async def root():
950
949
[INFO] [2022-08-28 12:00:00.000030] called dict
951
950
```
952
951
### 23. If you must use sync SDK, then run it in a thread pool.
953
-
If you must use an SDK to interact with external services, and it's not `async`,
952
+
If you must use a library to interact with external services, and it's not `async`,
954
953
then make the HTTP calls in an external worker thread.
955
954
956
955
For a simple example, we could use our well-known `run_in_threadpool` from starlette.
0 commit comments