@gasboost/client
v0.6.0
Published
Type-safe Google Apps Script RPC client with jobs and navigation.
Maintainers
Readme
@gasboost/client
Google Apps Script Web アプリケーション向けの、フレームワーク非依存クライアントライブラリです。
@gasboost/app で定義した RPC 契約を利用して、フロントエンドから型安全に Google Apps Script のサーバー関数を呼び出せます。
また、RPC や任意の非同期処理を Job として管理する仕組みと、Google Apps Script Web アプリケーションの History 同期機能を提供します。
インストール
pnpm add @gasboost/clientnpm:
npm install @gasboost/client型安全な RPC
バックエンドで @gasboost/app を使って RPC を定義します。
// backend/main.ts
import { AppsScript, type InferAppsScript } from "@gasboost/app";
const app = new AppsScript()
.call("sum", ({ a, b }: { a: number; b: number }) => a + b)
.call("getUser", async (id: string) => ({
id,
name: "Taro",
}));
export default app;
export type AppType = InferAppsScript<typeof app>;フロントエンドでは AppType を型として共有します。
import { appsScriptClient } from "@gasboost/client";
import type { AppType } from "../backend/main";
const { client } = appsScriptClient<AppType>();これにより、登録された RPC が型安全な関数として利用できます。
const total = await client.sum({ a: 1, b: 2 });
const user = await client.getUser("user-1");引数型と戻り値型は AppType から推論されます。
存在しない RPC や不正な引数は TypeScript 上で検出できます。
RPC metadata pipeline
業務 input と独立した RpcMetadata = Record<string, unknown> を全 RPC へ渡せます。client は metadata の意味を解釈せず、認証対象 RPC の判定もしません。認証の namespace / schema は auth 側の adapter が所有します。
import { appsScriptClient, type MetadataProvider } from "@gasboost/client";
const tracingMetadataProvider: MetadataProvider = () => ({
"@my-app/tracing": { traceId: crypto.randomUUID() },
});
const { client } = appsScriptClient<AppType>({
metadataProviders: [tracingMetadataProvider],
});
await client.sum({ a: 1, b: 2 });
await client.sum({ a: 1, b: 2 }, {
metadata: { "@my-app/tracing": { traceId: "explicit-trace" } },
});
// input が undefined の RPC も従来通り0引数で呼べます。
// metadata を渡す場合は input の位置を空けます。
await client.ping(undefined, { metadata: { "@my-app/tracing": { traceId: "ping" } } });metadata だけを付ける no-input RPC では、AppsScriptTransport は GAS の引数制約に合わせて google.script.run[name](null, metadata) を送ります(Google の引数仕様)。GAS global wrapper・dispatch・middleware の invocation.input・handler が受け取る値は null です。明示的な null 入力を維持するため、サーバー側では undefined に正規化しません。metadata も input も未指定なら従来通り0引数で送り、サーバーの input は undefined です。直接 dispatch(name, undefined, metadata) を呼ぶ場合も undefined を保持します。
provider は Job の実行時(queue 待機後・retry 時も含む)に評価します。各 provider の結果を登録順に shallow merge し、最後に per-call metadata を合成します。同じ namespace の値は丸ごと後の値で置き換えます。深い merge は行いません。provider が返す undefined はスキップし、全て未指定なら metadata を省略します。input と元の metadata object は変更しません。provider の例外は Job を失敗させ、Transport は呼び出しません。
RpcCallOptions は RPC の第2引数です。metadata は input 型にも result 型にも加わりません。
既存 client に scoped provider を追加するには withMetadataProviders を使います。
import { withMetadataProviders } from "@gasboost/client";
const scopedClient = withMetadataProviders(client, [tracingMetadataProvider]);appsScriptClient が生成した client 専用です。同じ RPC 型・Transport・Job Queue を保持し、元の client や別の scoped client の provider を変更しません。合成順は元の provider → scoped provider → per-call metadata です。
旧 createAuthenticatedClient と RPC 名リストは廃止しました。業務 handler は token を input として定義せず、必要な middleware が metadata を解釈します。
RPC Transport
@gasboost/client は RPC の実行先を Transport として抽象化しています。
デフォルトでは AppsScriptTransport が利用され、Google Apps Script が提供する google.script.run を通して RPC を実行します。
const { client } = appsScriptClient<AppType>();例えば、
await client.sum({ a: 1, b: 2 });はデフォルトでは対応する GAS のサーバー関数を、
google.script.run.sum({ a: 1, b: 2 })のように呼び出します。
成功時には RpcResponse.contents を JSON として parse し、その結果を返します。
GAS 側の失敗は Promise の reject としてそのまま伝播します。
Transport の差し替え
appsScriptClient() には任意の Transport を指定できます。
import { appsScriptClient, FetchTransport } from "@gasboost/client";
const { client } = appsScriptClient<AppType>({
transport: new FetchTransport({
endpoint: "/rpc",
}),
});Transport は次のインターフェースを持ちます。
interface Transport {
call(name: string, input?: unknown, metadata?: RpcMetadata): Promise<RpcResponse>;
}独自の Transport を実装することで、RPC の実行先を差し替えられます。
import type { RpcMetadata, RpcResponse, Transport } from "@gasboost/client";
class CustomTransport implements Transport {
public async call(name: string, input?: unknown, metadata?: RpcMetadata): Promise<RpcResponse> {
// 任意の RPC transport
}
}AppsScriptTransport
AppsScriptTransport は google.script.run を利用する標準 Transport です。
import { AppsScriptTransport } from "@gasboost/client";
const transport = new AppsScriptTransport();appsScriptClient() に transport を指定しなかった場合、自動的に AppsScriptTransport が利用されます。
FetchTransport
FetchTransport は HTTP endpoint に対して fetch で RPC を実行する Transport です。
import { FetchTransport } from "@gasboost/client";
const transport = new FetchTransport({
endpoint: "/rpc",
});RPC 名は endpoint の末尾に追加されます。
POST /rpc/{rpcName}引数は JSON body として送信されます。
{
"input": { "a": 1, "b": 2 }
}成功した HTTP response の body は RpcResponse.contents として扱われます。
HTTP endpoint がエラーを返した場合、次の形式の error response を利用できます。
{
"error": {
"name": "Error",
"message": "RPC failed",
"stack": "..."
}
}FetchTransport は特定の開発環境やサーバー実装には依存しません。
Vite、Cloud Run、Cloudflare Workers など、同じ RPC protocol を提供する任意の HTTP endpoint に利用できます。
Export
現在 @gasboost/client から公開されている主な API:
appsScriptClient;
withMetadataProviders;
RpcMetadata;
MetadataProvider;
RpcCallOptions;
AppsScriptTransport;
FetchTransport;
Transport;
RpcResponse;
AppsScriptJob;
AppsScriptJobStore;
AppsScriptHistoryPipeline;ローカル RPC について
@gasboost/vite の dev plugin は、Vite Dev Server 上に Local RPC endpoint を提供します。
POST /__gasboost/{rpcName}FetchTransport を利用することで、この endpoint に RPC を送信できます。
const { client } = appsScriptClient<AppType>({
transport: new FetchTransport({
endpoint: "/__gasboost",
}),
});ただし、@gasboost/client 自体は Vite やローカル開発環境を判定しません。
開発環境で FetchTransport を自動的に利用する仕組みは @gasboost/vite が担当します。
責務
@gasboost/client が担当するもの:
AppTypeに基づく型安全 RPC client- RPC Transport の抽象
google.script.runを利用するAppsScriptTransport- HTTP RPC を利用する
FetchTransport - JSON response の parse
- RPC / 非同期処理の Job Queue
- Job の状態管理
- pending Job の cancel
- Job の retry
- Job Store の購読
- GAS Container と iframe の History 同期
開発環境に応じた Transport の選択は @gasboost/vite が担当します。
React 固有の処理は @gasboost/react が担当します。
JSON Response
RPC のレスポンスは JSON として扱われます。
const user = await client.getUser("user-1");例えばバックエンドが、
{
id: "user-1",
name: "Taro",
}を返した場合、フロントエンドでも同じ構造の object として取得できます。
不正な JSON が返された場合は JSON.parse のエラーになります。
Date
Date は JavaScript の Date instance には復元されません。
例えばバックエンドが、
{
createdAt: new Date("2026-09-04T00:00:00.000Z"),
}を返した場合、クライアントでは、
{
createdAt: "2026-09-04T00:00:00.000Z",
}のように string として取得されます。
@gasboost/app の InferAppsScript も、この JSON シリアライズ後の型に合わせて Date を string として推論します。
appsScriptClient
const { client, jobs } = appsScriptClient<AppType>();appsScriptClient() は次の2つを返します。
{
client,
jobs,
}client
AppType から生成される型安全な RPC client です。
jobs
RPC と非同期処理の Job を管理します。
現在以下の API を提供します。
jobs.start(label, execute);
jobs.cancel(jobId);
jobs.retry(jobId);
jobs.subscribe(listener);
jobs.getSnapshot();Job
client 経由で実行した RPC は自動的に Job として管理されます。
const { client, jobs } = appsScriptClient<AppType>();
const promise = client.getUser("user-1");
const snapshot = jobs.getSnapshot();RPC 名が Job の label になります。
job.label === "getUser";Job には一意な id が割り当てられます。
Job Status
Job は次の状態を持ちます。
pending
running
success
failed状態は status から取得できます。
job.status;個別の判定メソッドも利用できます。
job.isPending();
job.isRunning();
job.isSuccess();
job.isFailed();Job は次の情報も保持します。
job.id;
job.label;
job.createdAt;
job.endedAt;
job.result;
job.error;Job の実行
RPC 以外の任意の非同期処理も、同じ Job Queue で管理できます。
const { jobs } = appsScriptClient<AppType>();
const result = await jobs.start("load-data", async () => {
return await loadData();
});client から実行された RPC と jobs.start() は同じ Queue / Runner を共有します。
並列実行
Job Runner は複数 Job の並列実行に対応しています。
現在の最大同時実行数は 30 です。
上限を超えた Job は pending として Queue に残り、実行中の Job が完了すると順次実行されます。
成功した Job
成功した Job は完了後に Job 一覧から削除されます。
jobs.getSnapshot();には、実行中・待機中・失敗した Job が主に残ります。
失敗した Job
失敗した Job は一覧に failed 状態で残ります。
const failedJob = jobs.getSnapshot().find((job) => job.status === "failed");エラーは job.error から取得できます。
Retry
失敗した Job は ID を指定して再実行できます。
jobs.retry(job.id);元の Job は一覧から削除され、同じ label と execute を利用した新しい Job が Queue に追加されます。
そのため、retry 後の Job は新しい ID を持ちます。
Cancel
pending 状態の Job はキャンセルできます。
jobs.cancel(job.id);キャンセルされた Job は Queue と Job 一覧から削除され、対応する Promise は AppsScriptJobCancelledError で reject されます。
すでに running になった Job はキャンセルされません。
現在の cancel は、実行開始前の Job を Queue から取り除くための機能です。
Job の購読
Job 一覧の変更を購読できます。
const unsubscribe = jobs.subscribe(() => {
console.log(jobs.getSnapshot());
});購読を解除する場合:
unsubscribe();getSnapshot() は現在の Job 一覧を返します。
const jobsSnapshot = jobs.getSnapshot();このインターフェースは React の useSyncExternalStore から直接利用できる形になっています。
React から利用する場合は @gasboost/react の useAppsScriptJob を利用できます。
History
AppsScriptHistoryPipeline は Google Apps Script Web アプリケーションの navigation state を同期するための仕組みです。
import { AppsScriptHistoryPipeline } from "@gasboost/client";Google Apps Script が提供する、
google.script.historygoogle.script.url
と iframe 側の History を同期します。
GAS Container History
↕
AppsScriptHistoryPipeline
↕
iframe History通常 React アプリケーションでは、直接利用する代わりに @gasboost/react の AppsScriptRouter を利用します。
関連パッケージ
@gasboost/app— バックエンドと RPC 契約の定義@gasboost/bridge— external frontend と GAS iframe 間の中継@gasboost/react— React integration
License
MIT
Coded RPC errors (0.6.0+)
組み込みの AppsScriptTransport / FetchTransport は、RPC失敗時に
公開型 AppsScriptRpcError をthrowします。任意のアプリ固有コードを判定できます。
import { AppsScriptRpcError } from "@gasboost/client";
try {
await client.createEmployee({ name: "Taro" });
} catch (error) {
if (error instanceof AppsScriptRpcError && error.code === "EMPLOYEE_ALREADY_EXISTS") {
// アプリ側で表示・復旧処理を選びます。
}
}App 5.5.0以降で明示したエラーは code と公開用 message を復元します。
GASはmessage内のバージョン付きwire形式、Fetchは同形式を運ぶ従来のViteレスポンスと
{ error: { code: string, message: string } } を受け付けます。
サーバースタックは復元しません。未知・不正な形式、コードなしの旧サーバーエラー、
ネットワーク失敗は INTERNAL_ERROR / An unexpected error occurred になります。
従来のname/message/stackのそのままの復元からの動作変更です。
AppとClientを一緒に更新してください。成功結果・型推論・カスタムTransportの契約は従来通りです。
