Routing

Caspian uses a high-performance file-system based router built on top of FastAPI. Your file structure defines your URL paths, making routing intuitive, type-safe, and async-native.

Core Concepts

All routes are defined inside the src/app directory. A route is simply a directory containing an index.html or index.py file.

File Path URL Path
src/app/index.html /
src/app/about/index.py /about
src/app/blog/posts/index.html /blog/posts

Async Page Logic (index.py)

For pages requiring metadata or server-side logic, use an index.py file. Since we run on FastAPI, the page() function should be async to prevent blocking the event loop.

src/app/index.py
from casp.page import load_page

title = "Caspian Documentation | The Native Python Web Framework"
description = "Explore the comprehensive documentation for Caspian..."

async def page():
    # You can perform non-blocking DB calls here
    return load_page(__file__)

Dynamic Routes

Caspian supports dynamic segments using square brackets. These are compiled into FastAPI path parameters (e.g., {id}) for maximum performance.

[ ]

Dynamic Segments

Wrap a folder name in brackets to create a variable segment.

src/app/users/[id]/index.py
Result: /users/123
[...]

Catch-all Segments

Use an ellipsis inside brackets to match multiple path segments.

src/app/docs/[...slug]/index.py
Result: /docs/getting-started/setup

Route Groups

You can organize your code without affecting the URL structure by wrapping a folder name in parentheses. This is useful for grouping related features (like auth) or opting specific routes out of a layout.

  • src/app/(auth)/login/index.py /login
  • src/app/(auth)/register/index.py /register

Layouts & Nesting

Layouts wrap your pages and preserve state during navigation. A layout is defined by a layout.html (and optional layout.py) file. Layouts nest automatically: a page inside /dashboard/settings will be wrapped by the Root layout, then the Dashboard layout.

src/app/layout.html
<!DOCTYPE html>
<html>
  <head>
    <title>{{ layout.title }}</title>
    <meta name="description" content="{{ layout.description }}" />
    <!-- Global CSS / Scripts -->
  </head>
  <body>
    <NavBar />
    
    <!-- Children are injected here -->
    {{ children | safe }}

  </body>
</html>

Async Layout Logic (layout.py)

If you need to fetch data for a layout (like user info for a sidebar), create a layout.py file next to your HTML. It must export an async def layout() function.

src/app/layout.py
from casp.auth import get_current_user

async def layout(context_data):
    # context_data contains URL params (e.g. id=123)
    # Use await for non-blocking auth/db checks
    user = await get_current_user()
    
    return {
        "user": user,
        "theme": "dark"
    }