Source URL:: [URL](https://fastapi.tiangolo.com/)
## Request Parameters
- Parameters are the inputs to the function, as variables. They are the inputs that are sent by whoever is requesting information.
### Path Parameters
- Path parameters are embedded in the path:
```python
@app.get("/items/{item_id}")
async def read_item(item_id):
return {"item_id": item_id}
```
- Type hint the params to convert them from their string representation.[^1]
- If you want to constrain the values, use an `Enum` and inherit from the base type that it needs to be converted to:
```python
class ModelName(str, Enum):
alexnet = "alexnet"
resnet = "resnet"
lenet = "lenet"
@app.get("/models/{model_name}")
async def get_model(model_name: ModelName):
if model_name is ModelName.alexnet:
return {"model_name": model_name, "message": "Deep Learning FTW!"}
```
- Import `fastapi.Path` to declare additional metadata.
- You can also use this to get around the fact that args with default values should be last in a function signature.
- You can add validations: `gt`, `le`
### Query Parameters
- Function arguments that are not part of the path are query parameters.
- You can make them optional by specifying `None` as a default value. This one will respond to `/items/1/?q=2`:
```python
@app.get("/items/{item_id}")
async def read_item(item_id: str, q: str | None = None):
if q:
return {"item_id": item_id, "q": q}
return {"item_id": item_id}
```
- As usual, use type annotations to convert the query parameters to the correct type.
- Use the `Query` object from `fastapi` to add additional validation to query parameters.
```python
@app.get("/items/")
async def read_items(q: str | None = Query(default=None, max_length=50)):
results = {"items": [{"item_id": "Foo"}, {"item_id": "Bar"}]}
if q:
results.update({"q": q})
return results
```
- Use ellipsis `…` or `pydantic.Required` as default value to declare it as a required variable.
- You can receive a list of things in query if it is specified more than once, like so: `/items/?q=foo&q=bar`.
- Use `alias` to specify an alternative name for the query parameter
- Use `Query(include_in_schema=False)` to exclude from the documentation
### Body Parameters
- Supply `{pydantic}` models in order to produce more complex types. Complex types will need to be sent via the request body.
```python
@app.put("/items/{item_id}")
async def create_item(item_id: int, item: Item, q: str | None = None):
result = {"item_id": item_id, **item.dict()}
if q:
result.update({"q": q})
return result
```
- The resolution is as follows:
- Declared in path? It's a Path Parameter
- Singular type (`int`, `float`, `str`, `bool`, etc.)? It's a query parameter
- Pydantic model? It's a body parameter.
- If there are more than one Pydantic models in the arguments, then it will need the overall JSON body to be composed of those two objects identified by the key.
- You can use `fastapi.Body` to specify singular objects as body parameters in addition to just the Pydantic models.
```python
@app.put("/items/{item_id}")
async def update_item(
*,
item_id: int,
item: Item,
user: User,
importance: int = Body(gt=0),
q: str | None = None
):
results = {"item_id": item_id, "item": item, "user": user, "importance": importance}
if q:
results.update({"q": q})
return results
```
### Cookie Parameters
- `Cookie()` can extract data from the Cookie header.
### Header Parameters
- `Header()` can extract data from the HTTP header.
- It automatically converts snake case to the kebab title case common in headers.
- In case of duplicate headers, then a list is resolved.
### Using `{pydantic}` models
- Use `pydantic.Field` to add addtiional validation to fields within objects that you use as parameters.
- Nested models can be done, such as lists, sets, tuples, or dicts.
- Other useful data types: `UUID`, `datetime.datetime`, `datetime.date`, `datetime.time`, `datetime.timedelta`, `frozenset`, `bytes`, `Decimal` and others defined by [Pydantic](https://pydantic-docs.helpmanual.io/usage/types/).
### Receiving form data
- If we are not received we may be receiving form data.
```python
@app.post("/login/")
async def login(username: str = Form(), password: str = Form()):
return {"username": username}
```
- You can't mix `Body` and `Form` fields in the same handler.
### Receiving files
- Use `fastapi.File` (bytes), or `fastapi.UploadFile` (streaming) to receive files.
```python
@app.post("/files/")
async def create_file(file: bytes = File(...)):
return {"file_size": len(file)}
```
## Errors
- At any point, you can `raise` an `Exception` to return an error to the client. If you want to return a specific status code, use `fastapi.HTTPException`.
```python
@app.get("/items/{item_id}")
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]}
```
- You can add custom headers to this `HTTPException` to supply more metadata.
- You can override the request validation error.
### Exception Handlers
- Exceptions are by default just raised and sent back to the client, but we can handle exceptions differently by registering exception handlers.
```python
class UnicornException(Exception):
def __init__(self, name: str):
self.name = name
@app.exception_handler(UnicornException)
async def unicorn_exception_handler(request: Request, exc: UnicornException):
return JSONResponse(
status_code=418,
content={"message": f"Oops! {exc.name} did something. There goes a rainbow..."},
)
```
## Response
- This is what is returned by the API. You need `{python-multipart}`
### Pydantic for the response
- Specify in the decorator's `response_model` argument the Pydantic model to which the return value will be coerced.
- You can use inheritance to differential the request and response model for CRUD applications.
- request model that is from the client
- output model that should be quite secure
- database model that needs to apply encryption at rest.
- Modify the response model in the decorator:
- `response_model_exclude_unset=True` means that default values are not included in the returned model
- `response_model_include={"name", "description"}` means that only the specified fields are included in the returned model.
- `response_model_exclude={"tax"}` means that the specified fields are excluded from the returned model.
## Decorator
- Status code can be set:
- in the `status_code` argument of the decorator, use `fastapi.status` as convenience functions to specify the codes.
- Tags can be set for the documentation. Use an enum to constrain tags.
- Summary and description for documentation.
- Response description for documentation in case docstring is inappropriately or too verbose.
## Dependencies
- Dependency injection means that the code declares some things that it needs to work and use ("inject" those dependencies).
- `fastapi.Depends(f)` can be used to declare a dependency callable `f` that is applied to the input parameters - it can be a function or a class. If it's a class, you can skip the `Depends(f)` argument if you already have the class instance in the type annotation.
- This is integrated with OpenAPI for that documentation.
- Dependencies can use more dependencies, and they are cached. Use `use_cache=False` to disable caching.
- You can declare `dependencies` argument in decorator to declare dependencies that don't get used (like verification).
- Global dependencies can be declared in `fastapi.APIRouter` and `fastapi.FastAPI` instances to apply across a group of handlers.
- Using `yield` in a dependency will allow you to run code after the handler is done. Sub dependencies can be generated with `generate_{name}()`.
- You should encapsulate yield in try blocks because exceptions won't be handled anymore.
## Security
- `fastapi.security` contains these utilities. It's essentially a set of middleware that applies some common security policies.
## Middleware
- Middleware is a function that works with every request before it is processed by the path operation.
- You can mutate the request and response after the path operations have run.
- It takes the request, call_next handlers, then returns the response.
- Integrated middleware
- `CORSMiddleware` is a middleware that takes some allowed origins, credentials, methods, and ehaders then only allows those to go through.
- `HTTPSRedirectMiddleware` is a middleware that redirects all HTTP requests to HTTPS.
- `TrustedHostMiddleware` is a middleware that only allows requests from trusted hosts.
- `GZipMiddleware` is a middleware that compresses responses with gzip.
## SQL Databases
- FastAPI is not tied to an ORM, we can use `{SQLAlchemy}` as a standard way this is done.
- The creator has created `{SQLModel}` to be more compatible with `{FastAPI}` and `{pydantic}`.
- [TODO: We'll learn this more later as we need it](https://fastapi.tiangolo.com/tutorial/sql-databases/#sql-relational-databases)
## Larger Applications
- Create a `fastapi.APIRouter` instance for each group of handlers, and put the different router.
- Unify them in `main.py` in the root module by `include_router`-ing them. This can be prefixed at a certain file so that everything is in that subpath.
- An `APIRouter` can also `include_router` another `APIRouter`.
## Background Tasks
- Run after returning a response, like queueing emails or processing data.
- Include an argument of class `fastapi.BackgroundTasks` to the handler, and then call `add_task` on it with the function to run in the background.
## Documentation
- Use `schema_extra` field in the `Config` class of a Pydantic models to define example data thatt the API can receive.
```python
class Item(BaseModel):
name: str
description: str | None
price: float
tax: float | None
class Config:
schema_extra = {
"example": {
"name": "Foo",
"description": "A very nice Item",
"price": 35.4,
"tax": 3.2,
}
}
```
- `Path`, `Query`, `Header`, `Cookie`, `Body`, `Form`, and `File` have examples that you can add.
- You can add metadata to the arguments of `FastAPI`.
## Static Files
- Mount a static files instance on a path to serve static files. This will not be included in the OpenAPI docs because it is completely independent.
```python
app.mount("/static", StaticFiles(directory="static"), name="static")
```
## Testing
- Create a `fastapi.testclient.TestClient` instance, that you can call just like `requests` client to test
## Advanced
- Return a response directly in order to:
- return a custom status code
- to dictate how to deserialize the response
- the `Response` object has attriubtes that can be set to modify the response.
- Other Responses
- `fastapi.responses.HTMLResponse` can be used to return HTML responses.
- `fastapi.responses.RedirectResponse` can be used to redirect to another URL.
- `fastapi.response.StreamingResponse` can be used to stream a response.
- `fastapi.responses.FileResponse` can be used to return a file.
- Custom Response classes
- Inherit from `fastapi.Response`, define the fields, and create a `render()` method
to output the text response to a `content` stream.
- Change the default response class in the instantiation of `FastAPI`.
- Mounting sub applications.
- Two distinct fastapi applications coexisting. No shared code as opposed to adding an `APIRouter`.
- Event handlers
- `@app.on_event()` can define a function that is run on startup, shutdown
## Response
- Take the `response: Response` as an argument and:
- use `set_cookie()` to set cookies.
- edit the `headers` field dict to edit the headers
- edit the `status_code` field to edit the status code
## Websocket
- Define the websocket endpoint at some path, and then loop over as we continue to receive websocket messages.
```python
@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}")
```
## Pydantic for configuration management
- Inherit from `BaseSettings` to create a class that can be used to manage configuration.
- You can then use this throughout the application.
## Concurrency
- `async def` is a coroutine that can be awaited.
- If it does not support `async await` then you cannot define an `async` path operation.
- This is very useful for longer running operations
## Deployment
- Pin your versions as breaking changes are common.
- Deploying in Containers
- Install the dependencies
- Run as normal, with host 0.0.0.0 and port
[^1]: Order does matter. If you define a path parameter after one the generally matches, it will always match on that one.