python 63 lines · 6 steps

Building a CRUD router with FastAPI

A self-contained projects router uses router-level auth, Pydantic schemas, and injected services to power four ownership-aware endpoints.

Explained by highlit
1from fastapi import APIRouter, Depends, HTTPException, status
2from pydantic import BaseModel
3 
4from .dependencies import get_current_user, get_project_service
5from .services import ProjectService
6from .models import User
7 
8router = APIRouter(
9 prefix="/projects",
10 tags=["projects"],
11 dependencies=[Depends(get_current_user)],
12 responses={404: {"description": "Project not found"}},
13)
14 
15 
16class ProjectIn(BaseModel):
17 name: str
18 description: str | None = None
19 
20 
21class ProjectOut(ProjectIn):
22 id: int
23 owner_id: int
24 
25 
26@router.get("", response_model=list[ProjectOut])
27async def list_projects(
28 user: User = Depends(get_current_user),
29 service: ProjectService = Depends(get_project_service),
30):
31 return await service.list_for_owner(user.id)
32 
33 
34@router.post("", response_model=ProjectOut, status_code=status.HTTP_201_CREATED)
35async def create_project(
36 payload: ProjectIn,
37 user: User = Depends(get_current_user),
38 service: ProjectService = Depends(get_project_service),
39):
40 return await service.create(owner_id=user.id, **payload.model_dump())
41 
42 
43@router.get("/{project_id}", response_model=ProjectOut)
44async def get_project(
45 project_id: int,
46 user: User = Depends(get_current_user),
47 service: ProjectService = Depends(get_project_service),
48):
49 project = await service.get(project_id)
50 if project is None or project.owner_id != user.id:
51 raise HTTPException(status.HTTP_404_NOT_FOUND, "Project not found")
52 return project
53 
54 
55@router.delete("/{project_id}", status_code=status.HTTP_204_NO_CONTENT)
56async def delete_project(
57 project_id: int,
58 user: User = Depends(get_current_user),
59 service: ProjectService = Depends(get_project_service),
60):
61 deleted = await service.delete(project_id, owner_id=user.id)
62 if not deleted:
63 raise HTTPException(status.HTTP_404_NOT_FOUND, "Project not found")
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Router-level dependencies apply a cross-cutting concern like authentication to every route without repeating it.
  2. 2Injecting a service keeps handlers thin, delegating persistence and business logic away from the HTTP layer.
  3. 3Enforcing ownership checks per resource turns a generic lookup into secure, tenant-scoped access.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Building a CRUD router with FastAPI — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code