PyxlePyxle/Docs

Data Loading

Pyxle uses @server decorated functions to load data on the server before rendering a page. The loader runs on every request, fetches whatever the page needs, and passes the result as props to the React component.

Basic loader

@server
async def load_page(request):
    return {"message": "Hello from the server!"}
export default function MyPage({ data }) {
  return <h1>{data.message}</h1>;
}

The @server function:

  1. Receives a Starlette `Request` object
  2. Must be async (enforced at compile time)
  3. Must return a JSON-serializable dict
  4. The return value is available as props.data in the React component

Accessing request data

The request parameter is a full Starlette Request. You have access to:

@server
async def load_page(request):
    # URL path parameters (from dynamic routes)
    user_id = request.path_params["id"]

    # Query string parameters
    page = request.query_params.get("page", "1")

    # Request headers
    auth = request.headers.get("authorization", "")

    # Cookies
    session = request.cookies.get("session_id", "")

    # The full URL
    url = str(request.url)

    return {"user_id": user_id, "page": int(page)}

Returning status codes

Return a tuple of (data, status_code) to set the HTTP status:

@server
async def load_page(request):
    item = await fetch_item(request.path_params["id"])
    if item is None:
        return {"error": "Not found"}, 404
    return {"item": item}

Error handling in loaders

Raise LoaderError to trigger the nearest error boundary:

from pyxle.runtime import LoaderError

@server
async def load_page(request):
    user = await fetch_user(request.path_params["id"])
    if user is None:
        raise LoaderError("User not found", status_code=404)
    if not user["active"]:
        raise LoaderError("Account suspended", status_code=403)
    return {"user": user}

When LoaderError is raised:

  1. Pyxle searches up the directory tree for the nearest error.pyxl
  2. If found, it renders the error boundary with the error context as props
  3. If not found, a default error page is shown

See Error Handling for full details.

Using external APIs

Loaders can call any Python code -- databases, APIs, file systems:

import httpx

@server
async def load_posts(request):
    async with httpx.AsyncClient() as client:
        resp = await client.get("https://jsonplaceholder.typicode.com/posts")
        resp.raise_for_status()
    return {"posts": resp.json()[:10]}
export default function PostsPage({ data }) {
  return (
    <ul>
      {data.posts.map(post => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  );
}

Pages without loaders

If a page has no @server function, data is an empty object:

export default function StaticPage() {
  return <h1>This page has no loader</h1>;
}

How it works

  1. A request hits a page route (e.g., /blog/hello-world)
  2. Pyxle imports the compiled Python module and runs the @server function
  3. The return value is serialised to JSON
  4. The React component is rendered on the server with { data: loaderResult } as props
  5. The full HTML is sent to the browser
  6. React hydrates the page on the client, using the same props embedded in the HTML

The loader runs on every request. There is no built-in caching -- use your own caching strategy in the loader if needed.

Next steps