Skip to content

Instantly share code, notes, and snippets.

@jph00
Created February 6, 2025 04:38
Show Gist options
  • Save jph00/9ed12338582731df6f3e133994e2f1d6 to your computer and use it in GitHub Desktop.
Save jph00/9ed12338582731df6f3e133994e2f1d6 to your computer and use it in GitHub Desktop.

Organizing FastHTML Applications Across Multiple Files

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.

1. Using Mount - The "Mini-Apps" Approach

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"))
    )

When to use Mount:

  • 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

2. Using APIRouter - The "Feature Organization" Approach

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!")
    )

When to use APIRouter:

  • 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
  1. 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")
])
  1. 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")

Choosing Between Mount and APIRouter

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment