To start with a FastAPI application, you first import the FastAPI class and create an instance of it. Then you use a decorator with a path operation along with a function that tells you what to do at a path operation. You also return something inside that function.
A path is something that is at the end of a URL, ex. https://notes.ishu.foo/something. The path here is /something. Operation here refers to any of the HTTP methods, such as GET, POST, PUT, DELETE, OPTIONS, HEAD, PATCH and TRACE. Thus the path operation decorator (app.get('/') below) tells FastAPI that the function below (the one being decorated) is in charge of handling requests that go to the path / using the operation get. Thus, this function is also known as a path operation function.
from fastapi import FastAPI
app = FastAPI()
@app.get("/")
async def root():
return { "message": "Hello world!" }FastAPI recommends that you use asyncdef in your code because it makes the operations a bit faster. It’s to be noted that even if you use normal defFastAPI still uses async operations under the hood to optimize for speed.
Path parameters
You can declare path parameters or variables with the following syntax:
@app.get('/item/{item_id}')
async def root(item_id: int):
return { "ITEM_ID": item_id }Note that there’s automatic data validation, so that /item/1 is valid while /item/foo is not, because item_id’s type is int. This data validation is handled by Pydantic.
Also worth noting is the fact that path operations are evaluated in order, so the declaration order matters here. For ex.
@app.get('/users/me')
async def read_user_me():
return { "user_id": "current user"}
@app.get('/users/{user_id}')
async def read_user(user_id: str):
return { "user_id": user_id}The former needs to be defined first, otherwise the path for that would match the latter’s declaration and it’d return user_id: me instead of user_id: the current user.
If you have a path operation that receives a path parameter, and you want the possible valid path parameter values to be predefined, you can use a python Enum.
Otherwise, if you want to use a direct path (i.e. /home/ishu/Downloads/) inside of your path parameter, you can use something like @app.get('/files/{file_path:path}').
Query parameters
When you declare function parameters that are not a part of path parameters, they are interpreted as query parameters. A query is nothing but the set of key-value pairs that go after the ? in a URL, separated by & characters. For ex., in the URL http://127.0.0.1:8000/items/?skip=0&limit=10 the query parameters are skipwith a value of 0 and limit with a value of 10.
ex.
@app.get("/items/")
async def read_item(skip: int = 0, limit: int = 10):
return fake_items_db[skip : skip + limit]here going to http://127.0.0.1:8000/items/ would be same as going to http://127.0.0.1:8000/items/?skip=0&limit=10, as we have added default values in the parameters. Of course, we can change the query param values and go to a different item. You can even have boolean values in the query parameter (here we had int values). You can make query parameters optional by setting the default as None.
Request body parameters
When you need to send data from a client, you send it as a request body. A response body is the data your API sends to the client. To declare a request body in FastAPI, you use a Pydantic model.
from fastapi import FastAPI
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str | None = None
price: float
tax: float | None = None
app = FastAPI()
@app.post("/items/")
async def create_item(item: Item):
item_dict = item.model_dump()
if item.tax is not None:
price_with_tax = item.price + item.tax
item_dict.update({"price_with_tax": price_with_tax})
return item_dict
# return item is also valid, but here we wanted to update the tax, so we added itCORS Middleware
One of the primary points of contention you will reach when you’re trying to communicate with your FastAPI endpoint is a message like this:
Access to fetch at ‘http://127.0.0.1:8000/videos/’ from origin ‘http://localhost:5173’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource.
This refers to CORS - situations where a frontend running in a browser has JavaScript code that communincates with a backend, and the backend is in a different “origin” than the frontend.
An origin is the combination of protocol (http or https), domain (ishu.foo, localhost, app.ishu.foo), and a port (80, 8000, 5173). As such, http://localhost, https://localhost (mind the ‘s’), http://localhost:8080 are all different origins even if they’re all in localhost.
To achieve the frontend-backend handshake the backend must have a list of “allowed origins”. It’s also possible to declare the list as a * (a “wildcard”) to say taht all are allowed. But that only allows certain types of communication, excluding everything that involves credentials, such as Cookies, Authorization, headers with Bearer Tokens, etc.
For everything to work correctly, it’s better to specify a list of allowed origings.
You can configure your FastAPI application using CORSMiddleware.
ex.
from fastapi import FastAPI
from fastapi.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=["*"],
)
@app.get("/")
async def main():
return {"message": "Hello World"}The following arguments are supported:
allow_origins: list of origins that are allowed to make cross-origin requests.allow_origin_regex: regex string to match against origins that should be permitted, ex.https://.*\.example\.org.allow_methods: a list of HTTP methods that should be allowed; defaults to['GET']. You can use the wildcard*to allow all methods, as done above.allow_headers: a list of HTTP headers that should be supported; defaults to[].allow_credentials: indicates that cookies should be supported for cross-origin requests. Defaults tofalse. If it’s set totrue, none ofallow_origins,allow_methodsandallow_headerscan be set to['*']. All of them must be explicitly specified.expose_headers: indicates any response headers that should be made accessible to the browser; defaults to[].max_age: max time in seconds for browsers to cache CORS responses; defaults to600.