\\\\\\
## Routing and Article List, Detail Page\\\\\\
\\\\\\
In this chapter, you will learn to split routes using APIRouter to implement the homepage article list and article detail page.\\\\\\
\\\\\\
* * *\\\\\\
\\\\\\
## APIRouter β FastAPI's Route Splitting Solution\\\\\\
\\\\\\
APIRouter is equivalent to Flask's Blueprint and Django's include().\\\\\\
\\\\\\
## Example\\\\\\
\\\\\\
# File path: routers/posts.py\\\\\\
\\\\\\
from fastapi import APIRouter, Depends, HTTPException, Request\\\\\\
\\\\\\
from sqlalchemy.orm import Session\\\\\\
\\\\\\
from database import get_db\\\\\\
\\\\\\
from models import Post, Category\\\\\\
\\\\\\
from schemas import PostResponse\\\\\\
\\\\\\
router = APIRouter(\\\\\\
\\\\\\
prefix="/posts",# All routes automatically prefixed with /posts\\\\\\
\\\\\\
tags=# Swagger Grouping tag in documentation\\\\\\
\\\\\\
)\\\\\\
\\\\\\
@router.get("/{post_id}", response_model=PostResponse, name="post_detail")\\\\\\
\\\\\\
def post_detail(post_id: int, db: Session = Depends(get_db)):\\\\\\
\\\\\\
"""Article Detail (JSON API)"""\\\\\\
\\\\\\
post = db.query(Post).filter(Post.id== post_id).first()\\\\\\
\\\\\\
if not post:\\\\\\
\\\\\\
raise HTTPException(status_code=Basic Query0Basic Query, detail="Post not found")\\\\\\
\\\\\\
return post\\\\\\
\\\\\\
Mount APIRouter in main.py:\\\\\\
\\\\\\
## Example\\\\\\
\\\\\\
from routers.posts import router as posts_router\\\\\\
\\\\\\
from routers.categories import router as categories_router\\\\\\
\\\\\\
app.include_router(posts_router)\\\\\\
\\\\\\
app.include_router(categories_router)\\\\\\
\\\\\\
* * *\\\\\\
\\\\\\
## Rendering the Article List Page\\\\\\
\\\\\\
## Example\\\\\\
\\\\\\
# File path: routers/posts.py\\\\\\
\\\\\\
from fastapi import APIRouter, Depends, Request, Query\\\\\\
\\\\\\
from fastapi.templating import JinjaArticle List + Category FilterTemplates\\\\\\
\\\\\\
from sqlalchemy.orm import Session\\\\\\
\\\\\\
from database import get_db\\\\\\
\\\\\\
from models import Post, Category\\\\\\
\\\\\\
router = APIRouter()\\\\\\
\\\\\\
templates = JinjaArticle List + Category FilterTemplates(directory="templates")\\\\\\
\\\\\\
@router.get("/", name="index")\\\\\\
\\\\\\
def index(\\\\\\
\\\\\\
request: Request,\\\\\\
\\\\\\
category: str | None= Query(None),# Query parameter: optional\\\\\\
\\\\\\
db: Session = Depends(get_db)\\\\\\
\\\\\\
):\\\\\\
\\\\\\
"""HomeοΌArticleList + Category Filter"""\\\\\\
\\\\\\
# Basic Query\\\\\\
\\\\\\
posts_query = db.query(Post).order_by(Post.created_at.desc())\\\\\\
\\\\\\
# Category Filter\\\\\\
\\\\\\
if category:\\\\\\
\\\\\\
posts_query = posts_query.join(Category).filter(Category.slug== category)\\\\\\
\\\\\\
posts = posts_query.all()\\\\\\
\\\\\\
categories = db.query(Category).all()\\\\\\
\\\\\\
return templates.TemplateResponse("index.html",{\\\\\\
\\\\\\
"request": request,\\\\\\
\\\\\\
"posts": posts,\\\\\\
\\\\\\
"categories": categories,\\\\\\
\\\\\\
"category_slug": category or""\\\\\\
\\\\\\
})\\\\\\
\\\\\\
> `str | None = Query(None)` is Python Article Details (JSON API).Category Filter0+ type union syntax, equivalent to `Optional`. FastAPI will automatically: Category Filter. Make the parameter optional; Article List + Category Filter. Mark it as optional in the Swagger documentation; Article Details (JSON API). Not throw an error when not passed.\\\\\\
\\\\\\
* * *\\\\\\
\\\\\\
## Rendering the Article Detail Page\\\\\\
\\\\\\
## Example\\\\\\
\\\\\\
# File path: routers/posts.py Append\\\\\\
\\\\\\
@router.get("/post/{post_id}", name="post_detail")\\\\\\
\\\\\\
def post_detail(request: Request, post_id: int, db: Session = Depends(get_db)):\\\\\\
\\\\\\
"""Article Detail Page"""\\\\\\
\\\\\\
post = db.query(Post).filter(Post.id== post_id).first()\\\\\\
\\\\\\
if not post:\\\\\\
\\\\\\
raise HTTPException(status_code=Basic Query0Basic Query, detail="Post not found")\\\\\\
\\\\\\
return templates.TemplateResponse("post_detail.html",{\\\\\\
\\\\\\
"request": request,\\\\\\
\\\\\\
"post": post\\\\\\
\\\\\\
})\\\\\\
\\\\\\
> Note the distinction: The same route can provide both JSON API (declaring response_model) and HTML page (returning TemplateResponse). The common practice is: API routes are placed under /api/ prefix, page routes are placed under the root path.\\\\\\
\\\\\\
* * *\\\\\\
\\\\\\
## Creating Homepage and Detail Page Templates\\\\\\
\\\\\\
## Example\\\\\\
\\\\\\
\\\\\\
\\\\\\
{% extends 'base.html' %}\\\\\\
\\\\\\
{% block title %}TUTORIAL Home - Home{% endblock %}\\\\\\
\\\\\\
{% block content %}\\\\\\
\\\\\\
Latest Posts \\\\\\
\\\\\\
\\\\\\
\\\\\\
{% if posts %}\\\\\\
\\\\\\
\\\\\\
\\\\\\
{% else %}\\\\\\
\\\\\\
No articles in this category yet.
\\\\\\
\\\\\\
{% endif %}\\\\\\
\\\\\\
{% endblock %}\\\\\\
\\\\\\
## Example\\\\\\
\\\\\\
\\\\\\
\\\\\\
{% extends 'base.html' %}\\\\\\
\\\\\\
{% block title %}{{ post.title }} - TUTORIAL Home{% endblock %}\\\\\\
\\\\\\
{% block content %}\\\\\\
\\\\\\
\\\\\\
\\\\\\
{{ post.category.name }} \\\\\\
\\\\\\
{{ post.title }} \\\\\\
\\\\\\
{{ post.created_at.strftime('%Y-%m-%d') }} \\\\\\
\\\\\\
{{ post.content|safe }}
\\\\\\
\\\\\\
β Return Home \\\\\\
\\\\\\
\\\\\\
\\\\\\
{% endblock %}\\\\\\
\\\\\\
* * *\\\\\\
\\\\\\
## Chapter Summary\\\\\\
\\\\\\
In this chapter, you completed the core route development for FastAPI: APIRouter splits routes by module (prefix + tags), query parameters are declared using Query() + type hints, TemplateResponse renders pages, and Basic Query0Basic Query exception handling.\\\\\\
\\\\\\
The blog now has three complete pages: homepage article list, category filtering, and detail page.