コンテンツにスキップ

検索はプロダクションビルドでのみ利用可能です。 ローカルでテストするには、サイトをビルドしてプレビューしてください。

Native RPC & Service Bindings

Speakeasy では全てのサービス間通信に Cloudflare Workers Native RPC を使用しています。HTTP リクエストを介さず、Service Binding 経由で TypeScript メソッドを直接呼び出します。


項目REST API (HTTP)Native RPC
通信方式HTTP リクエスト直接メソッド呼び出し
レイテンシネットワークラウンドトリップゼロレイテンシ(同一データセンター内)
型安全性OpenAPI / 手動型定義TypeScript 型がそのまま適用
シリアライズJSON 変換が必要自動(V8 構造化クローン)
エラーハンドリングHTTP ステータスコードTypeScript 例外

呼び出し元バインディング呼び出し先
CounterPROOFProof(認証)
CounterVINTAGEVintage(連絡先)
CounterFIZZFizz(チャット・配信)
CounterBASEBase(組織・店舗)
CounterBELLBell(通知)
PenthousePROOFProof(認証)
PenthouseVINTAGEVintage(連絡先)
PenthouseBASEBase(組織・店舗)
呼び出し元バインディング呼び出し先
DoorPROOFProof(認証)
TapPROOFProof(認証)
TapFIZZFizz(チャット)
TapBASEBase(組織)
TapVINTAGEVintage(連絡先)
呼び出し元バインディング呼び出し先主な用途
VintageFIZZFizzWebSocket 通知(notify, broadcast
FizzVINTAGEVintageグループ情報取得
BasePROOFProofユーザー・セッション管理
BaseBELLBell通知送信

各サービスは WorkerEntrypoint クラスを継承し、public メソッドを RPC インターフェースとして公開します。

services/fizz/src/index.ts
import { WorkerEntrypoint } from 'cloudflare:workers';
export default class FizzWorker extends WorkerEntrypoint<Env> {
// HTTP フォールバック(Direct HTTP アクセス用)
override async fetch(req: Request): Promise<Response> {
return app.fetch(req, this.env, this.ctx);
}
// Native RPC メソッド
async listChatRooms(params: {
readonly shopId: string;
readonly userId: string;
readonly userType: 'shop' | 'customer';
readonly limit?: number;
readonly cursor?: string;
}) {
// 実装...
}
async sendChatMessage(params: {
readonly roomId: string;
readonly senderId: string;
readonly senderType: 'shop' | 'customer';
readonly content: string;
}) {
// 実装...
}
}

2. クライアント側: RPC クライアント型

Section titled “2. クライアント側: RPC クライアント型”

アプリ側では Service Binding を型安全に利用するためのクライアント型を定義します。

apps/counter/src/lib/fizz-rpc.ts
import { getRequestEvent } from '$app/server';
/** Fizz サービスの RPC クライアント型 */
export type FizzClient = {
listChatRooms(params: {
readonly shopId: string;
readonly userId: string;
readonly userType: 'shop' | 'customer';
readonly limit?: number;
readonly cursor?: string;
}): Promise<ChatRoomsResult>;
sendChatMessage(params: {
readonly roomId: string;
readonly senderId: string;
readonly senderType: 'shop' | 'customer';
readonly content: string;
}): Promise<ChatMessage>;
// ...
};
/** Service Binding から Fizz クライアントを取得 */
export const getFizzClient = (): FizzClient => {
const { platform } = getRequestEvent();
if (!platform?.env?.FIZZ) {
throw new Error('FIZZ service binding not available');
}
return platform.env.FIZZ as unknown as FizzClient;
};

SvelteKit Remote Functions がサーバーサイドで RPC クライアントを使用します。

apps/counter/src/lib/chat.remote.ts
import { query, command } from '$app/server';
import { z } from 'zod';
import { getFizzClient } from './fizz-rpc';
export const getChatRooms = query(
z.object({
limit: z.number().optional(),
cursor: z.string().optional()
}),
async ({ limit, cursor }) => {
const client = getFizzClient();
// Native RPC 呼び出し(HTTP なし)
const res = await client.listChatRooms({
shopId: getShopId(),
userId: getUserId(),
userType: 'shop',
limit,
cursor
});
return res;
}
);

apps/counter/wrangler.jsonc
{
"services": [
{ "binding": "PROOF", "service": "proof" },
{ "binding": "VINTAGE", "service": "vintage" },
{ "binding": "FIZZ", "service": "fizz" },
{ "binding": "BASE", "service": "base" },
{ "binding": "BELL", "service": "bell" }
],
"env": {
"dev": {
"services": [
{ "binding": "PROOF", "service": "proof-dev", "remote": true },
{ "binding": "VINTAGE", "service": "vintage-dev", "remote": true },
{ "binding": "FIZZ", "service": "fizz-dev", "remote": true },
{ "binding": "BASE", "service": "base-dev", "remote": true },
{ "binding": "BELL", "service": "bell-dev", "remote": true }
]
}
}
}
  • 本番環境では同一アカウント内の Workers 間で直接バインド
  • 開発環境では "remote": true を指定してリモート Workers に接続

ブラウザ (Svelte コンポーネント)
↓ Remote Function 呼び出し
SvelteKit サーバー (Remote Function)
↓ getFizzClient() / getVintageClient() 等
Service Binding (platform.env.FIZZ)
↓ Native RPC (メソッド直接呼び出し)
WorkerEntrypoint (FizzWorker)
↓ Drizzle ORM / Durable Objects / Queues
PostgreSQL (Hyperdrive) / KV / R2

この構成により、ブラウザからデータベースまで HTTP リクエストを一切介さず、全てが Cloudflare のインフラ内で完結します。