Concepts
Mutating Data
Forms submit to server/api endpoints where input is validated with Zod before it reaches the database or Python layer. The RPC boundary types the call both ways.
Forms
Submit to /api endpoints. Input is validated before it reaches the
database or Python.
export default function Login() {
return (
<form action="/api/auth/login" method="POST">
<input name="email" type="email" required />
<input name="password" type="password" required />
<button type="submit">Log in</button>
</form>
);
}
Validation
Validation is Zod, at the server boundary. The schema is typed, so what the client sends is what the server checked.
import { z } from "zod";
const schema = z.object({
email: z.string().email(),
password: z.string().min(8),
});
export default defineEventHandler(async (event) => {
const body = await readValidatedBody(event, schema);
// body is typed as { email: string; password: string }
// ... authenticate user
});
To Python
Mutations that need AI or background processing call Python through the RPC boundary. The same typed contract — no manual serialization.
import { chat } from "@bionicjs/core/server/ai";
export default defineEventHandler(async (event) => {
const { message } = await readBody(event);
const reply = await chat(message);
return { reply };
});