The RPC boundary
Communication, generation, and the contract Python exposes.
Communication
The boundary between TypeScript and Python in BionicJS is bridged using RPC. Instead of manually writing fetch calls, developers define their Python functions and BionicJS automatically generates fully-typed TypeScript client stubs.
1React component
2 | await generateSummary(text) // typed stub
3 v
4Nitro server (server/api handler)
5 | proxied /rpc/* request
6 v
7Python process (FastAPI RPC surface)
8 | runs the real function
9 v
10result returns with full types
When you call await generateSummary(text) in your React component, it calls the Nitro server, which in turn forwards the RPC call to the Python process, returning the result with full end-to-end type safety.
Generation
The client stubs are generated, not maintained by hand. The Python function signatures are the source of truth, and BionicJS turns them into TypeScript types and callable functions:
1def generate_summary(text: str, max_words: int = 100) -> str:
2 """Returns a summary of the text."""
3 ...
1const summary = await generateSummary({
2 text: "a long document",
3 maxWords: 80,
4});
5// summary: string - checked at compile time
Argument and return types are inferred from the Python signature. A change on the Python side is reflected in the TypeScript type the moment the stubs regenerate - there is no hand-synced schema to drift.
The contract
The RPC boundary is the product BionicJS sells. Where the glued pair (Next.js + FastAPI) has untyped JSON, BionicJS has a contract checked both ways:
- Functions are called by their real names, not URL strings.
- Payloads are validated before the Python function runs.
- Return values carry Typescript types all the way back to the component.
- A wrong argument fails at compile time, not at runtime.
20 / 25