"serde is the translator standing on the bridge, turning Rust structs into JSON and back without you writing a line of parsing."
Derive It and Forget It
Anything that crosses the IPC bridge must become plain data. In Rust, the crate that does this is serde. You annotate a struct or enum with #[derive(Serialize, Deserialize)] and serde generates the conversion code: Serialize lets it travel to the frontend (as a command's return), Deserialize lets it arrive from the frontend (as a command's argument). No manual JSON building, ever.
Structs Both Ways
A command can take a struct and return a struct. The frontend sends a plain object that deserializes into your Rust struct; the command returns a struct that serializes into a plain object the webview receives. This is how rich data crosses cleanly — you think in typed shapes on both ends, and serde handles the wire format in the middle.
Make the Field Names Match the Frontend
Rust structs use snake_case fields (created_at), but your TypeScript probably wants camelCase (createdAt). Add #[serde(rename_all = "camelCase")] to the struct and serde renames every field on the wire. Now your Rust stays idiomatic and your frontend receives the casing it expects — the same philosophy as the argument convention, applied to data shapes.