WebSockets
このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →
サーバーとの通信の全部または一部にWebSocketsを利用できます。クライアント側での設定方法についてはwsLinkを参照してください。
このドキュメントではWebSockets利用に特化した詳細を説明しています。サブスクリプションの一般的な使用方法についてはサブスクリプションガイドをご覧ください。
WebSocketサーバーの作成
bashyarn add ws
bashyarn add ws
server/wsServer.tstsimport {applyWSSHandler } from '@trpc/server/adapters/ws';import {WebSocketServer } from 'ws';import {appRouter } from './routers/app';import {createContext } from './trpc';constwss = newWebSocketServer ({port : 3001,});consthandler =applyWSSHandler ({wss ,router :appRouter ,createContext ,// Enable heartbeat messages to keep connection open (disabled by default)keepAlive : {enabled : true,// server ping message interval in millisecondspingMs : 30000,// connection is terminated if pong message is not received in this many millisecondspongWaitMs : 5000,},});wss .on ('connection', (ws ) => {console .log (`++ Connection (${wss .clients .size })`);ws .once ('close', () => {console .log (`-- Connection (${wss .clients .size })`);});});console .log ('WebSocket Server listening on ws://localhost:3001');process .on ('SIGTERM', () => {console .log ('SIGTERM');handler .broadcastReconnectNotification ();wss .close ();});
server/wsServer.tstsimport {applyWSSHandler } from '@trpc/server/adapters/ws';import {WebSocketServer } from 'ws';import {appRouter } from './routers/app';import {createContext } from './trpc';constwss = newWebSocketServer ({port : 3001,});consthandler =applyWSSHandler ({wss ,router :appRouter ,createContext ,// Enable heartbeat messages to keep connection open (disabled by default)keepAlive : {enabled : true,// server ping message interval in millisecondspingMs : 30000,// connection is terminated if pong message is not received in this many millisecondspongWaitMs : 5000,},});wss .on ('connection', (ws ) => {console .log (`++ Connection (${wss .clients .size })`);ws .once ('close', () => {console .log (`-- Connection (${wss .clients .size })`);});});console .log ('WebSocket Server listening on ws://localhost:3001');process .on ('SIGTERM', () => {console .log ('SIGTERM');handler .broadcastReconnectNotification ();wss .close ();});
TRPCClientでWebSocketsを使用する設定
Linksを使用して、クエリやミューテーションをHTTP転送、サブスクリプションをWebSocket経由でルーティングできます。
client.tstsximport {createTRPCClient ,createWSClient ,wsLink } from '@trpc/client';import type {AppRouter } from './server';// create persistent WebSocket connectionconstwsClient =createWSClient ({url : `ws://localhost:3001`,});// configure TRPCClient to use WebSockets transportconstclient =createTRPCClient <AppRouter >({links : [wsLink ({client :wsClient ,}),],});
client.tstsximport {createTRPCClient ,createWSClient ,wsLink } from '@trpc/client';import type {AppRouter } from './server';// create persistent WebSocket connectionconstwsClient =createWSClient ({url : `ws://localhost:3001`,});// configure TRPCClient to use WebSockets transportconstclient =createTRPCClient <AppRouter >({links : [wsLink ({client :wsClient ,}),],});
認証 / 接続パラメータ
Webアプリケーションを開発している場合、リクエストの一部としてクッキーが送信されるため、このセクションは無視して構いません。
WebSocketsでの認証を行うには、createWSClientにconnectionParamsを定義します。これはクライアントがWebSocket接続を確立する際の最初のメッセージとして送信されます。
server/context.tstsimport type {CreateWSSContextFnOptions } from '@trpc/server/adapters/ws';export constcreateContext = async (opts :CreateWSSContextFnOptions ) => {consttoken =opts .info .connectionParams ?.token ;// [... authenticate]return {};};export typeContext =Awaited <ReturnType <typeofcreateContext >>;
server/context.tstsimport type {CreateWSSContextFnOptions } from '@trpc/server/adapters/ws';export constcreateContext = async (opts :CreateWSSContextFnOptions ) => {consttoken =opts .info .connectionParams ?.token ;// [... authenticate]return {};};export typeContext =Awaited <ReturnType <typeofcreateContext >>;
client/trpc.tstsimport {createTRPCClient ,createWSClient ,wsLink } from '@trpc/client';import type {AppRouter } from './server';importsuperjson from 'superjson';constwsClient =createWSClient ({url : `ws://localhost:3000`,connectionParams : async () => {return {token : 'supersecret',};},});export consttrpc =createTRPCClient <AppRouter >({links : [wsLink ({client :wsClient ,transformer :superjson })],});
client/trpc.tstsimport {createTRPCClient ,createWSClient ,wsLink } from '@trpc/client';import type {AppRouter } from './server';importsuperjson from 'superjson';constwsClient =createWSClient ({url : `ws://localhost:3000`,connectionParams : async () => {return {token : 'supersecret',};},});export consttrpc =createTRPCClient <AppRouter >({links : [wsLink ({client :wsClient ,transformer :superjson })],});
tracked()を使用したIDの自動追跡(推奨)
tracked()ヘルパーを使用してイベントをyieldし、idを含めることで、クライアントは切断時に自動的に再接続し、再接続時に最後に認識されたIDをlastEventId入力の一部として送信します。
サブスクリプションの初期化時にlastEventIdを送信すると、ブラウザがデータを受信するたびに自動的に更新されます。
lastEventIdに基づいてデータを取得する場合、すべてのイベントの捕捉が重要であるときは、当社のフルスタックSSE例のようにReadableStreamや類似パターンを中間層として使用し、lastEventIdに基づく元のバッチをyieldしている間に新しく発行されるイベントが無視されるのを防ぐことを検討してください。
tsimportEventEmitter , {on } from 'events';import {initTRPC ,tracked } from '@trpc/server';import {z } from 'zod';typePost = {id : string;title : string };constt =initTRPC .create ();constpublicProcedure =t .procedure ;constrouter =t .router ;constee = newEventEmitter ();export constsubRouter =router ({onPostAdd :publicProcedure .input (z .object ({// lastEventId is the last event id that the client has received// On the first call, it will be whatever was passed in the initial setup// If the client reconnects, it will be the last event id that the client receivedlastEventId :z .string ().nullish (),}).optional (),).subscription (async function* (opts ) {if (opts .input ?.lastEventId ) {// [...] get the posts since the last event id and yield them}// listen for new eventsfor await (const [data ] ofon (ee , 'add', {// Passing the AbortSignal from the request automatically cancels the event emitter when the subscription is abortedsignal :opts .signal ,})) {constpost =data asPost ;// tracking the post id ensures the client can reconnect at any time and get the latest events since this idyieldtracked (post .id ,post );}}),});
tsimportEventEmitter , {on } from 'events';import {initTRPC ,tracked } from '@trpc/server';import {z } from 'zod';typePost = {id : string;title : string };constt =initTRPC .create ();constpublicProcedure =t .procedure ;constrouter =t .router ;constee = newEventEmitter ();export constsubRouter =router ({onPostAdd :publicProcedure .input (z .object ({// lastEventId is the last event id that the client has received// On the first call, it will be whatever was passed in the initial setup// If the client reconnects, it will be the last event id that the client receivedlastEventId :z .string ().nullish (),}).optional (),).subscription (async function* (opts ) {if (opts .input ?.lastEventId ) {// [...] get the posts since the last event id and yield them}// listen for new eventsfor await (const [data ] ofon (ee , 'add', {// Passing the AbortSignal from the request automatically cancels the event emitter when the subscription is abortedsignal :opts .signal ,})) {constpost =data asPost ;// tracking the post id ensures the client can reconnect at any time and get the latest events since this idyieldtracked (post .id ,post );}}),});
WebSockets RPC仕様
詳細は以下のTypeScript定義を参照してください:
query / mutation
リクエスト
tsinterfaceRequestMessage {id : number | string;jsonrpc ?: '2.0';method : 'query' | 'mutation';params : {path : string;input ?: unknown; // <-- pass input of procedure, serialized by transformer};}
tsinterfaceRequestMessage {id : number | string;jsonrpc ?: '2.0';method : 'query' | 'mutation';params : {path : string;input ?: unknown; // <-- pass input of procedure, serialized by transformer};}
レスポンス
...下記、またはエラー
tsinterfaceResponseMessage {id : number | string;jsonrpc ?: '2.0';result : {type : 'data'; // always 'data' for mutation / queriesdata :TOutput ; // output from procedure};}
tsinterfaceResponseMessage {id : number | string;jsonrpc ?: '2.0';result : {type : 'data'; // always 'data' for mutation / queriesdata :TOutput ; // output from procedure};}
subscription / subscription.stop
サブスクリプションの開始
tsinterfaceSubscriptionRequest {id : number | string;jsonrpc ?: '2.0';method : 'subscription';params : {path : string;input ?: unknown; // <-- pass input of procedure, serialized by transformer};}
tsinterfaceSubscriptionRequest {id : number | string;jsonrpc ?: '2.0';method : 'subscription';params : {path : string;input ?: unknown; // <-- pass input of procedure, serialized by transformer};}
サブスクリプションのキャンセルにはsubscription.stopを呼び出します
tsinterfaceSubscriptionStopRequest {id : number | string; // <-- id of your created subscriptionjsonrpc ?: '2.0';method : 'subscription.stop';}
tsinterfaceSubscriptionStopRequest {id : number | string; // <-- id of your created subscriptionjsonrpc ?: '2.0';method : 'subscription.stop';}
サブスクリプションのレスポンス形式
...下記、またはエラー
tsinterfaceSubscriptionResponse {id : number | string;jsonrpc ?: '2.0';result :| {type : 'data';data :TData ; // subscription emitted data}| {type : 'started'; // subscription started}| {type : 'stopped'; // subscription stopped};}
tsinterfaceSubscriptionResponse {id : number | string;jsonrpc ?: '2.0';result :| {type : 'data';data :TData ; // subscription emitted data}| {type : 'started'; // subscription started}| {type : 'stopped'; // subscription stopped};}
接続パラメータ
接続が?connectionParams=1で初期化される場合、最初のメッセージは接続パラメータでなければなりません。
tsinterfaceConnectionParamsMessage {data :Record <string, string> | null;method : 'connectionParams';}
tsinterfaceConnectionParamsMessage {data :Record <string, string> | null;method : 'connectionParams';}
エラー
https://www.jsonrpc.org/specification#error_objectまたはエラーフォーマットを参照してください。
サーバーからクライアントへの通知
{ id: null, type: 'reconnect' }
サーバーシャットダウン前にクライアントに再接続を指示します。wssHandler.broadcastReconnectNotification()によって呼び出されます。