Routes and Parameters
Now that we have a running FastAPI app, let's look at how to handle more complex URLs.
We'll build out endpoints to interact with "Projects". Since we haven't introduced a database yet, we'll use a simple Python dictionary to simulate our data, and a small Pydantic model to describe the shape of a project.
Update the imports and add the ProjectRead model and mock_database dict to src/release_tracker/main.py. The full file should now look like this:
from fastapi import FastAPI
from pydantic import BaseModel
class ProjectRead(BaseModel):
id: int
name: str
slug: str
app = FastAPI(title="Release Tracker API")
# A simple mock database for now
mock_database: dict[int, ProjectRead] = {
1: ProjectRead(id=1, name="Frontend Redesign", slug="frontend-redesign"),
2: ProjectRead(id=2, name="API v2", slug="api-v2"),
3: ProjectRead(id=3, name="Database Migration", slug="database-migration"),
}
ProjectRead is a regular Pydantic model. We'll use it both as the value type in our mock database and as the response shape FastAPI sends back to clients.
Path Parameters
Path parameters are variables embedded directly into the URL path. You use them when you want to identify a specific resource, like a specific project.
Add this route below the mock_database in main.py:
@app.get("/projects/{project_id}")
def get_project(project_id: int):
return mock_database.get(project_id)
Type Checking in Action
Notice the type hint project_id: int.
If a user requests /projects/2, FastAPI will automatically convert the string "2" from the URL into a Python integer before passing it to your function.
If a user requests /projects/hello, FastAPI will automatically intercept the request and return a 422 Unprocessable Entity error because "hello" is not a valid integer. You didn't have to write any error handling code for that!
Query Parameters
Query parameters are added to the end of the URL after a ?, like /projects?name=API+v2. They are typically used for filtering, sorting, or searching.
If you declare a function parameter that is not in the URL path, FastAPI automatically interprets it as a query parameter.
Add this route below get_project in main.py:
@app.get("/projects")
def list_projects(name: str | None = None):
projects = list(mock_database.values())
if name is None:
return projects
return [p for p in projects if p.name == name]
In this example:
nameis a query parameter.- It has a default value of
None, making it optional. - If you go to
/projects, you get all three projects. - If you go to
/projects?name=API+v2, you get just the matching project.
FastAPI handles parsing, validating, and converting query string values completely behind the scenes.