📊

TauriのIPCにGraphQLを使う

に公開

はじめに

TauriでWebアプリケーションをつくるときには、IPCを使う機会がありますが、型情報をフロントエンド側とバックエンド側で共有して高速に開発を進めたいところです。
RSPCを使う方法がありましたが、開発が終了してしまいました。

https://github.com/specta-rs/rspc/discussions/351

そこで、この記事では、GraphQL( async-grpahql )を使う方法を紹介します。

実装方針

  • ipcの名前は graphql とします(何でもいいです)。
  • Queryのみ実装します(MutationはQueryと同じように実装できるのでスキップ、Subscriptionは難しいのでやりません)。
  • Rust側の実装は async-graphql を用います。

やりかた

Rust側の実装

async-graphql を使えるプラグインが公開されているが、更新が止まっていて、最新のTauriには対応していません。
https://github.com/JonasKruckenberg/tauri-plugin-graphql

上記プラグインがなくても、比較的簡単に導入できます。
async_graphql の多くの構造体が serdeSerializeDeserialize トレイトを実装しているため、そのまま各所に指定することが可能です。

use async_graphql::{ EmptyMutation, EmptySubscription, Object, Schema, Variables };

type Query;

#[Object]
impl Query {
  async fn greet(&self, name: String) -> String {
    format!("Hello, {}", name)
  }
}


type MySchema = Schema<Query, EmptyMutation, EmptySubscription>;

#[tauri::command]
async fn graphql(query: String, variables: Variables, state: tauri::State<MySchema>) -> tauri::Result<async_graphql::Response, ()> {
  let schema = state.0;
  let mut request = async_graphql::Request::new(query);
  request.variables = variables;
  schema.execute(request).await
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
  let schema = Schema::new(Query, EmptyMutation, EmptySubscription);
  tauri::Builder::default()
    .manage(schema)
    .invoke_handler(tauri::generate_handler![graphql])
    .run(tauri::generate_context!())
    .expect("error while running tauri application");
}

JavaScript側の実装

Tauri の invoke 関数を使います。

今のプロジェクトでは gql.tada を使っているので、以下の例でもそれを用いたものになっています。また、以下の記事を参考にして、引数がある場合は2個の引数、定義されていない場合は1個の引数となるようにしています。

https://the-guild.dev/graphql/codegen/docs/guides/vanilla-typescript#type-safe-graphql-operation-execution

import { invoke } from '@tauri-apps/api/core'
import type { TadaDocumentNode } from 'gql.tada'
import { type ExecutionResult, type GraphQLError, print } from 'graphql'

export const requestToGraphQL = <TResult, TVariables>(
  query: TadaDocumentNode<TResult, TVariables>,
  ...[variables]: TVariables extends Record<string, never> ? [] : [TVariables]
): Promise<ExecutionResult<TResult>> => {
  return invoke<ExecutionResult<TResult>>('graphql', {
    query: print(query),
    variables,
  })
}

おわりに

このようにして、IPCにGraphQLを使うことができました。ぜひ参考にしてみてください。

Discussion