😈

Tauriで型安全なクライアントを生成する

2024/06/17に公開

A

tauri-spectaを使う

https://github.com/oscartbeaumont/tauri-specta/blob/main/docs/v1.md

※Tauri v1での使い方を書きますが、v2でも問題なくできると思います。

使い方(既存のtauriプロジェクトに追加)

1. tauri-spectaの追加

cargo add specta@1
cargo add tauri-specta@1 --features javascript,typescript

2. #[specta::specta]を適応する

#[tauri::command]
+ #[specta::specta]
fn get_config() -> Result<Config, Error> {
    config::get()
}

3. #derive[specta::Type]する

- #[derive(Clone, Serialize, Deserialize, Debug)]
+ #[derive(Clone, Serialize, Deserialize, Debug, specta::Type)]
pub struct Memo {
    pub id: i32,
    pub content: String,
    pub updated_at: String,
    pub created_at: String,
    pub tags: Vec<models::Tag>,
}

この例だとmodels::Tagにも#deriveしてあげる必要がある

4. mainでexportする

このようにts::export(collect_types![])に入れます。
collect_types!に入れる時点でエラーが起きた場合、ハンドラの返り値か引数にspecta::Typeがderiveされていないない可能性があります。もう一度3.にいって確認してください。

また以下の例ではproject-root/src/bindings.tsという形でtsファイルが出力されます。

main関数で行う場合は「main実行」->「src/bindings.ts更新」->「tauriがコードのソース変更検知」->「main実行」の無限ループに陥るため忘れずに.taurignoreに追加してください

testで追加する例

main.rs
#[test]
fn export_bindings(){
+    #[cfg(debug_assertions)]
+    ts::export(
+        collect_types![
+            get_file_text,
+            edit_memo,
+            delete_memo,
+            get_memo,
+            get_memo_list,
+            add_memo,
+            get_config,
+            set_config,
+            save_config,
+        ],
+        "../src/bindings.ts",
+    )
+    .unwrap();
}

感想

バックエンドとフロントエンドのバリデーションをしなくてよいという環境は、罪悪感とともに圧倒的なストレスフリーな環境を用意してくれます。
TypeScriptではResultが使えないのでエラーハンドリングが不安になるのですが、そこはしっかりTypeScriptを書くときはTypeScriptの考え方をして書いたほうがよいという結論になりました。
具体的にはすべてをResult型で考えないよう、tanstack-queryをつかってデータフェッチ時のエラーはonErrorに書いて、それ以外のエラーに関してはどうせテストを書くのでそこで担保、あとはすこしだけneverthrowに頼るという形に落ち着いています。

Discussion