Let's explore two main approaches to splitting FastHTML applications into multiple files: using Mount
and using APIRouter
. I'll explain the pros and cons of each and provide practical examples.
Mount
is ideal when you want to create semi-independent sub-applications that could potentially be reused across different projects. Think of these as "mini-apps" that handle specific features.
# blog/routes.py
from fasthtml import fast_app, Titled, P
blog_app, rt = fast_app()
@rt("/", name="list")
def blog_home():
return Titled("Blog Posts",
P("Latest posts will appear here"))
@rt("/post/{id}", name="detail")
def blog_post(id: int):
return Titled(f"Post {id}",
P(f"This is post {id}"))
# auth/routes.py
auth_app, rt = fast_app()
@rt("/login", name="login")
def login():
return Titled("Login",
P("Login form here"))
# main.py
from fasthtml import fast_app, Mount, Titled, P, A
from blog.routes import blog_app
from auth.routes import auth_app
app, rt = fast_app(routes=[
Mount("/blog", blog_app, name="blog"),
Mount("/auth", auth_app, name="auth")
])
@rt("/")
def home():
return Titled("My Site",
P(A(link=uri("blog:list"))("Visit the Blog")),
P(A(link=uri("auth:login"))("Login"))
)
- For larger features that are relatively self-contained
- When you might want to reuse the component in other projects
- If you need different middleware or configurations for different parts of your app
- For third-party FastHTML apps you want to integrate
APIRouter
is perfect for organizing related routes within the same application context. It's lighter weight than Mount
and is ideal for logical grouping of endpoints.
# views/products.py
from fasthtml import APIRouter, Titled, P
products_router = APIRouter(prefix="/products")
@products_router("/", name="list")
def product_list():
return Titled("Products",
P("Product listing here"))
@products_router.get("/{id}", name="detail")
def product_detail(id: int):
return Titled(f"Product {id}",
P(f"Details for product {id}"))
# views/cart.py
cart_router = APIRouter(prefix="/cart")
@cart_router("/", name="view")
def view_cart():
return Titled("Shopping Cart",
P("Cart contents here"))
# main.py
from fasthtml import fast_app
from views.products import products_router
from views.cart import cart_router
app, rt = fast_app()
# Add routers to main app
products_router.to_app(app)
cart_router.to_app(app)
@rt("/")
def home():
return Titled("Shop Home",
P("Welcome to our shop!")
)
- For organizing related routes within the same application
- When you want to maintain shared application state
- For simpler route organization without full application separation
- When routes are closely related but you want to keep the code organized
- Combining Both Approaches
You can use both methods together - Mount for major features and APIRouter for organizing routes within those features:
# apps/shop/routes.py
shop_app, rt = fast_app()
products_router = APIRouter(prefix="/products")
cart_router = APIRouter(prefix="/cart")
# Add routers to shop app
products_router.to_app(shop_app)
cart_router.to_app(shop_app)
# main.py
app, rt = fast_app(routes=[
Mount("/shop", shop_app, name="shop"),
Mount("/blog", blog_app, name="blog")
])
- URL Generation Remember that the way you generate URLs will differ slightly:
# For mounted apps
A(link=uri("blog:list"))("Blog")
# For APIRouter routes
A(link=products_router.rt_funcs.list)("Products")
Use Mount when:
- You need complete separation of concerns
- The feature could be a standalone application
- You want different middleware/configurations
- You're integrating third-party components
Use APIRouter when:
- You're just organizing related routes
- You want to share application state easily
- The routes are part of the same feature
- You want simpler code organization