Proofread README.md (#67)

Minor proofreading
This commit is contained in:
Fernando
2025-04-11 04:30:41 -04:00
committed by GitHub
parent 5555113176
commit fe3f4fcfb8

View File

@@ -3,7 +3,7 @@ Opinionated list of best practices and conventions I use in startups.
For the last several years in production, For the last several years in production,
we have been making good and bad decisions that impacted our developer experience dramatically. we have been making good and bad decisions that impacted our developer experience dramatically.
Some of them are worth sharing. Some of them are worth sharing.
## Contents <!-- omit from toc --> ## Contents <!-- omit from toc -->
- [Project Structure](#project-structure) - [Project Structure](#project-structure)
@@ -111,7 +111,7 @@ from src.posts.constants import ErrorCode as PostsErrorCode # in case we have S
``` ```
## Async Routes ## Async Routes
FastAPI is an async framework, in the first place. It is designed to work with async I/O operations and that is the reason it is so fast. FastAPI is an async framework, in the first place. It is designed to work with async I/O operations and that is the reason it is so fast.
However, FastAPI doesn't restrict you to use only `async` routes, and the developer can use `sync` routes as well. This might confuse beginner developers into believing that they are the same, but they are not. However, FastAPI doesn't restrict you to use only `async` routes, and the developer can use `sync` routes as well. This might confuse beginner developers into believing that they are the same, but they are not.
@@ -123,8 +123,8 @@ from executing the tasks.
- If the route is defined `async` then it's called regularly via `await` - If the route is defined `async` then it's called regularly via `await`
and FastAPI trusts you to do only non-blocking I/O operations. and FastAPI trusts you to do only non-blocking I/O operations.
The caveat is if you fail that trust and execute blocking operations within async routes, The caveat is that if you violate that trust and execute blocking operations within async routes,
the event loop will not be able to run the next tasks until that blocking operation is done. the event loop will not be able to run subsequent tasks until the blocking operation completes.
```python ```python
import asyncio import asyncio
import time import time
@@ -186,7 +186,7 @@ The second caveat is that operations that are non-blocking awaitables or are sen
- Awaiting CPU-intensive tasks (e.g. heavy calculations, data processing, video transcoding) is worthless since the CPU has to work to finish the tasks, - Awaiting CPU-intensive tasks (e.g. heavy calculations, data processing, video transcoding) is worthless since the CPU has to work to finish the tasks,
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. 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.
- Running CPU-intensive tasks in other threads also isn't effective, because of [GIL](https://realpython.com/python-gil/). - Running CPU-intensive tasks in other threads also isn't effective, because of [GIL](https://realpython.com/python-gil/).
In short, GIL allows only one thread to work at a time, which makes it useless for CPU tasks. In short, GIL allows only one thread to work at a time, which makes it useless for CPU tasks.
- If you want to optimize CPU intensive tasks you should send them to workers in another process. - If you want to optimize CPU intensive tasks you should send them to workers in another process.
**Related StackOverflow questions of confused users** **Related StackOverflow questions of confused users**
@@ -346,7 +346,7 @@ If we didn't put data validation to dependency, we would have to validate `post_
for every endpoint and write the same tests for each of them. for every endpoint and write the same tests for each of them.
### Chain Dependencies ### Chain Dependencies
Dependencies can use other dependencies and avoid code repetition for the similar logic. Dependencies can use other dependencies and avoid code repetition for similar logic.
```python ```python
# dependencies.py # dependencies.py
from fastapi.security import OAuth2PasswordBearer from fastapi.security import OAuth2PasswordBearer
@@ -474,7 +474,7 @@ Developing RESTful API makes it easier to reuse dependencies in routes like thes
2. `GET /courses/:course_id/chapters/:chapter_id/lessons` 2. `GET /courses/:course_id/chapters/:chapter_id/lessons`
3. `GET /chapters/:chapter_id` 3. `GET /chapters/:chapter_id`
The only caveat is to use the same variable names in the path: The only caveat is having to use the same variable names in the path:
- If you have two endpoints `GET /profiles/:profile_id` and `GET /creators/:creator_id` - If you have two endpoints `GET /profiles/:profile_id` and `GET /creators/:creator_id`
that both validate whether the given `profile_id` exists, but `GET /creators/:creator_id` that both validate whether the given `profile_id` exists, but `GET /creators/:creator_id`
also checks if the profile is creator, then it's better to rename `creator_id` path variable to `profile_id` and chain those two dependencies. also checks if the profile is creator, then it's better to rename `creator_id` path variable to `profile_id` and chain those two dependencies.
@@ -510,11 +510,16 @@ async def get_user_profile_by_id(
``` ```
### FastAPI response serialization ### FastAPI response serialization
If you think you can return Pydantic object that matches your route's `response_model` to make some optimizations, You may think you can return Pydantic object that matches your route's `response_model` to make some optimizations,
then it's wrong. but you'd be wrong.
FastAPI first converts that pydantic object to dict with its `jsonable_encoder`, then validates
data with your `response_model`, and only then serializes your object to JSON.
This means your Pydantic model object is created twice:
- First, when you explicitly create it to return from your route.
- Second, implicitly by FastAPI to validate the response data according to the response_model.
FastAPI firstly converts that pydantic object to dict with its `jsonable_encoder`, then validates
data with your `response_model`, and only then serializes your object to JSON.
```python ```python
from fastapi import FastAPI from fastapi import FastAPI
from pydantic import BaseModel, root_validator from pydantic import BaseModel, root_validator