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:
- Receives a Starlette `Request` object
- Must be
async(enforced at compile time) - Must return a JSON-serializable
dict - The return value is available as
props.datain 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:
- Pyxle searches up the directory tree for the nearest
error.pyxl - If found, it renders the error boundary with the error context as props
- 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
- A request hits a page route (e.g.,
/blog/hello-world) - Pyxle imports the compiled Python module and runs the
@serverfunction - The return value is serialised to JSON
- The React component is rendered on the server with
{ data: loaderResult }as props - The full HTML is sent to the browser
- 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
- Mutate data with server actions: Server Actions
- Handle errors gracefully: Error Handling