Routes
FastAPI is an extremely powerful framework for building APIs in Python. In this note, we will dive deeper into the core concepts of FastAPI, covering Basic Route Handling, Path Parameters, Query Parameters, and Request Body, as well as how to return appropriate HTTP status codes.
Basic Route Handling in FastAPI
In FastAPI, route handling refers to defining functions that are triggered when a client sends an HTTP request to a specific URL. These routes are decorated with HTTP method decorators, such as @app.get()
, @app.post()
, @app.put()
, etc., which indicate which HTTP methods the route will handle.
Example 1: Basic Route Handling
from fastapi import FastAPI
app = FastAPI()
# A simple GET route
@app.get("/")
def read_root():
return {"message": "Hello, World!"}
# A POST route to create a resource
@app.post("/create")
def create_item(item: dict):
return {"message": "Item created successfully", "item": item}
- GET Route (
@app.get("/")
): This route will respond toGET
requests to the root URL (/
). When accessed, it returns a simple JSON response. - POST Route (
@app.post("/create")
): This route listens forPOST
requests at/create
and accepts a JSON payload, which is returned as part of the response.
Example 2: Returning Different Status Codes
In FastAPI, you can return different status codes like 201 Created
, 204 No Content
, etc., to indicate the outcome of the operation. To customize the status code, you can use the status_code
parameter.
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from fastapi import status
app = FastAPI()
# Pydantic model to validate the request body
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
# A POST route that creates an item and returns a 201 status code
@app.post("/items/", status_code=status.HTTP_201_CREATED)
def create_item(item: Item):
return {"message": "Item created", "item": item}
# A DELETE route that returns 204 status code (No Content) after deleting an item
@app.delete("/items/{item_id}", status_code=status.HTTP_204_NO_CONTENT)
def delete_item(item_id: int):
return None # No content is returned, just status code 204
- 201 Created: The
create_item
route returns a201 Created
status code when a new item is successfully created. This is indicated usingstatus_code=status.HTTP_201_CREATED
. - 204 No Content: The
delete_item
route deletes an item and returns a204 No Content
status code. This means the request was successful, but there's no content to return.
Path Parameters
Path parameters are dynamic segments in the URL that allow you to capture data from the URL itself. Path parameters are used for identifying specific resources, like user IDs or item IDs. In FastAPI, these parameters are defined by placing them inside curly braces {}
in the route path.
Example 1: Using Path Parameters
@app.get("/items/{item_id}")
def read_item(item_id: int):
return {"item_id": item_id}
- Path Parameter (
{item_id}
): In this example, the routeGET /items/{item_id}
expects an integer parameteritem_id
in the URL, which is passed to theread_item()
function.
Example 2: Using Multiple Path Parameters
@app.get("/users/{user_id}/items/{item_id}")
def get_user_item(user_id: str, item_id: int):
return {"user_id": user_id, "item_id": item_id}
- Multiple Path Parameters: This route captures two parameters from the URL:
user_id
(a string) anditem_id
(an integer).
Example Request:
GET /items/10
would return{"item_id": 10}
.GET /users/john/items/20
would return{"user_id": "john", "item_id": 20}
.
FastAPI will validate the types of path parameters automatically. If the type doesn't match, FastAPI will return a 422 Unprocessable Entity
error.
Query Parameters
Query parameters are optional parameters appended to the URL after a question mark (?
). They are commonly used for filtering, sorting, or modifying the request. In FastAPI, query parameters are passed as function arguments.
Example 1: Using Query Parameters
@app.get("/search")
def search_items(q: str = None, limit: int = 10):
return {"query": q, "limit": limit}
q
: This query parameter is used for searching. It's optional and defaults toNone
.limit
: This query parameter limits the number of results returned. It has a default value of10
.
Example Request:
GET /search?q=laptop&limit=5
would return{"query": "laptop", "limit": 5}
.GET /search
(no parameters) would return{"query": null, "limit": 10}
.
Example 2: Validating Query Parameters
You can validate query parameters by defining them with type annotations.
@app.get("/items")
def get_items(limit: int = 10, skip: int = 0):
return {"limit": limit, "skip": skip}
- Type Validation: FastAPI automatically validates the types of query parameters. In this case,
limit
must be an integer, andskip
must be an integer.
Example Request:
GET /items?limit=20&skip=5
would return{"limit": 20, "skip": 5}
.
If you pass invalid types, FastAPI will return a 422 Unprocessable Entity
error with details about the validation issue.
Request Body
Request bodies are used to send data to the server, typically in POST, PUT, or PATCH requests. FastAPI uses Pydantic models to define and validate the structure of the request body. The request body is parsed into Python objects automatically by FastAPI.
Example 1: Creating a Pydantic Model for Request Body
from pydantic import BaseModel
class Item(BaseModel):
name: str
description: str = None
price: float
tax: float = None
@app.post("/items/")
def create_item(item: Item):
return {"message": "Item created", "item": item}
- Pydantic Model (
Item
): TheItem
class defines the structure of the request body. The attributesname
,description
,price
, andtax
are defined, wherename
andprice
are required. - Request Body: The
create_item
function accepts an instance ofItem
as the request body. FastAPI automatically parses the incoming JSON data into this model.
Example Request:
{
"name": "Laptop",
"price": 999.99,
"tax": 19.99
}
Example 2: Returning a Different Status Code for Created Resource
You can use FastAPI’s status_code
parameter to customize the HTTP response status code.
from fastapi import status
@app.post("/items/", status_code=status.HTTP_201_CREATED)
def create_item(item: Item):
return {"message": "Item created successfully", "item": item}
- 201 Created: This will return a
201 Created
status code, indicating that a new resource was successfully created.
Example 3: Using Request Body with Optional Fields
class User(BaseModel):
username: str
email: str
age: int = None # Optional field
@app.post("/users/")
def create_user(user: User):
return {"message": "User created", "user": user}
- Optional Fields: In this case, the
age
field is optional. If it is not provided in the request body, it will default toNone
.
Example Request:
{
"username": "john_doe",
"email": "john@example.com"
}
This request would return:
{
"message": "User created",
"user": {
"username": "john_doe",
"email": "john@example.com",
"age": null
}
}
Example 4: Returning 400 Error for Invalid Input
FastAPI will automatically validate the request body. If the input data does not match the expected schema, FastAPI will return a 422 Unprocessable Entity
error.
For example, if the request body is missing the required name
field:
{
"price": 999.99
}
This will return:
{
"detail": [
{
"loc": ["body", "name"],
"msg": "field required",
"type": "value_error.missing"
}
]
}