Concepts
Fetching Data
Data travels over /api. In development Vite proxies it to Nitro; in production Nitro serves it directly. Python functions are exposed through the generated typed RPC boundary.
From the client
Client code fetches over /api with generated typed stubs. In development
Vite proxies to Nitro; in production Nitro serves it directly.
import { useQuery } from "@tanstack/react-query";
export default function Dashboard() {
const { data } = useQuery({
queryKey: ["users"],
queryFn: () => fetch("/api/users").then((r) => r.json()),
});
return <div>{data?.map((u) => <p key={u.id}>{u.name}</p>)}</div>;
}
From the server
Nitro handlers read and return typed data. They can call Python through the RPC boundary when the answer needs the intelligence layer.
import { db } from "@bionicjs/core/server";
export default defineEventHandler(async () => {
return await db.query.users.findMany();
});
From Python
Python functions are exposed through generated RPC stubs, so fetching happens over the same typed contract from either side.
from bionicjs import ai
async def chat(message: str) -> str:
client = ai.client()
response = await client.chat.completions.create(
model="claude-sonnet-4-5",
messages=[{"role": "user", "content": message}],
)
return response.choices[0].message.content