メインコンテンツまでスキップ

Client Connection の管理

backend から client の接続と切断をリアルタイムで検出し、対応します。​

Client Connections を使用する理由​

ユーザーが online かどうかの把握に依存する機能を構築しているとします。たとえば、presence indicator を表示する chat app、アクティブな参加者を追跡する collaborative editor、接続済み player を表示する game lobby などです。

connection tracking がない場合、heartbeat polling またはカスタム WebSocket logic を実装する必要があります。Squid の client connection handler を使用すると、単一の function を decorate するだけで、発生した state change に対応できます。

// Backend: react to connection changes
@clientConnectionStateHandler()
async onConnectionChange(clientId: ClientId, state: ClientConnectionState): Promise<void> {
if (state === 'DISCONNECTED') {
await this.squid.collection('presence').doc(clientId).update({ status: 'offline' });
}
}

// Frontend: check connection status
const isConnected = squid.connectionDetails().connected;

polling は不要です。カスタム WebSocket management も不要です。必要なのは decorator だけです。

概要​

Client connections により、client が Squid server に接続した、切断した、または削除されたことを backend で検出できます。各 client には接続時に一意の clientId が割り当てられます。これを使用して presence を追跡し、resource を cleanup し、または workflow を trigger できます。

Client connections を使用する場合​

ユースケース推奨事項
ユーザーの online/offline status を追跡するClient connections
ユーザーが離脱したときに resource を cleanup するClient connections
database change に対応するTriggers を使用
schedule に従って code を実行するSchedulers を使用

仕組み​

  1. client が Squid に接続し、一意の clientId が割り当てられます
  2. backend handler が CONNECTED state で呼び出されます
  3. client が切断した場合(例: browser tab を閉じた場合)、handler が DISCONNECTED で呼び出されます
  4. 一定期間後も client が再接続しない場合、Squid は clientId を削除し、REMOVED を指定して handler を呼び出します
  5. 削除後に client が再接続した場合、新しい clientId が割り当てられます

クイックスタート​

前提条件​

  • squid init で初期化された Squid backend project
  • NPM からインストールされた @squidcloud/backend package

ステップ 1: Connection handler を作成する​

service class に connection state handler を追加します。

Backend code
import { SquidService, clientConnectionStateHandler } from '@squidcloud/backend';
import { ClientConnectionState, ClientId } from '@squidcloud/client';

export class ExampleService extends SquidService {
@clientConnectionStateHandler()
async onConnectionChange(clientId: ClientId, state: ClientConnectionState): Promise<void> {
console.log(`Client ${clientId} is now ${state}`);
}
}

ステップ 2: backend を開始または deploy する​

ローカル開発では、Squid CLI を使用して backend をローカルで実行します。

squid start

cloud に deploy するには、backend の deployを参照してください。

ステップ 3: client で connection status を監視する​

Client code
import { filter } from 'rxjs';

// Check if currently connected
const isConnected = squid.connectionDetails().connected;

// Get the current client's ID
const clientId = squid.connectionDetails().clientId;

// React to connection changes
squid
.connectionDetails()
.observeConnected()
.pipe(filter(Boolean))
.subscribe(() => {
console.log('Connected with client ID:', squid.connectionDetails().clientId);
});

コアコンセプト​

Connection state​

各 client は次の state を遷移します。

State意味
CONNECTEDclient が server に接続した直後です
DISCONNECTEDclient は切断されましたが、再接続時に備えて Squid は clientId を保持します
REMOVEDclient は十分長く切断されていたため、Squid が clientId を破棄しました。次回の接続では新しい ID が割り当てられます。

Client ID​

すべての client は接続時に一意の clientId を受け取ります。この ID は次の特性を持ちます。

  • frontend では squid.connectionDetails().clientId を介して利用可能
  • backend では this.context.clientId を介して利用可能(connection handler だけでなく任意の backend function で利用可能)
  • 短時間の disconnect をまたいで stable(REMOVED になるまで同じ ID が保持されます)
  • auth provider の userId とは異なります。必要に応じて両者を mapping する必要があります。

@clientConnectionStateHandler decorator​

decorate された function は 2 つの argument を受け取ります。

ParameterType説明
clientIdClientId(string)state が change した client の ID
connectionStateClientConnectionState'CONNECTED'、'DISCONNECTED'、'REMOVED' のいずれか

function は void または Promise<void> を返せます。

Frontend connection API​

connection 情報には squid.connectionDetails() を介してアクセスします。

Property / MethodReturn Type説明
.connectedbooleanclient が現在接続されているかどうか
.clientIdstringこの connection に割り当てられた一意の client ID
.observeConnected()Observable<boolean>接続時は true、切断時は false を emit します
注記

Squid client は WebSocket connection を lazy に確立します。つまり、.snapshots() による query への subscribe など、operation が必要としたときにのみ server に接続します。.connected または .observeConnected() を単独で呼び出しても connection は開始されません。status を確認する前に client の接続を確実に行う必要がある場合は、まず data を subscribe するか、connection を trigger する operation を実行してください。

Passive(HTTP-only)mode​

serverless function や短時間実行 script のように、persistent WebSocket が望ましくない environment では、isPassiveMode: true を指定して client を初期化します。passive mode では client は WebSocket を開かず、HTTP のみで通信します。

Backend code
const squid = new Squid({
appId: 'YOUR_APP_ID',
region: 'YOUR_REGION',
environmentId: 'dev',
apiKey: 'YOUR_API_KEY',
isPassiveMode: true,
});

HTTP-based operation は通常どおり動作します。executeFunction、executeWebhook、native query、one-shot AI ask、その他の同様の call が対象です。realtime connection が必要な operation は、This operation is not available in passive mode. Use active mode for real-time features. を throw します。これには以下が含まれます。

  • Realtime query および document subscription(.snapshots())
  • squid.job().awaitJob() による job の待機
  • Queue consumption、distributed lock、notification observable
  • observeStatusUpdates() による agent status update
  • connectionDetails().observeConnected()

backend code では、通常の getSquid() と並んで getPassiveSquid() が専用の passive-mode instance を返します。これらは分離されているため、2 つの mode が connection を共有することはありません。

エラー処理​

Handler error​

@clientConnectionStateHandler function が error を throw した場合、server-side には log されますが、client の connection には影響しません。silent failure を避けるため、handler 内で error を処理してください。

Backend code
@clientConnectionStateHandler()
async onConnectionChange(clientId: ClientId, state: ClientConnectionState): Promise<void> {
try {
await this.updatePresence(clientId, state);
} catch (error) {
console.error(`Failed to update presence for ${clientId}:`, error);
}
}

よくある問題​

問題原因解決策
Handler が呼び出されないService が export されていない、または backend が deploy されていないservice が service/index.ts で export されていることを確認し、squid deploy を実行します
clientId が予期せず change する長時間の disconnect 後に client が削除されたREMOVED state を使用して cleanup し、CONNECTED で再度 mapping します
observeConnected() が throw するclient が passive mode であるSquid client が active(default)mode で初期化されていることを確認します

ベストプラクティス​

  1. 接続時に clientId を userId に mapping します。 clientId は connection-scoped、userId は auth-scoped であるため、client 接続時に mapping を insert すれば、connection から user を lookup できます。
  2. DISCONNECTED だけでなく REMOVED で cleanup します。 DISCONNECTED client は同じ clientId で再接続する可能性があります。resource を削除するのは state が REMOVED の場合だけにしてください。
  3. handler logic は高速に保ちます。 connection state handler は全 client の接続・切断ごとに実行されます。高コストな operation は避けるか、非同期で chain してください。
  4. ユーザーの識別には authentication を使用します。 clientId だけではユーザーが誰かは分かりません。auth と組み合わせて presence feature を構築してください。

コード例​

User presence tracking​

この例では、presence collection 内で clientId を userId に mapping して、online のユーザーを追跡します。

Frontend: 接続時に presence を登録する

Client code
import { filter } from 'rxjs';

interface PresenceData {
userId: string;
status: 'online' | 'offline';
}

// When connected, insert a presence record linking clientId to userId
squid
.connectionDetails()
.observeConnected()
.pipe(filter(Boolean))
.subscribe(() => {
const clientId = squid.connectionDetails().clientId;
squid
.collection<PresenceData>('presence')
.doc(clientId)
.insert({ userId: currentUserId, status: 'online' });
});

Backend: disconnect/removal 時に presence を更新する

Backend code
import { SquidService, clientConnectionStateHandler } from '@squidcloud/backend';
import { ClientConnectionState, ClientId } from '@squidcloud/client';

export class PresenceService extends SquidService {
@clientConnectionStateHandler()
async handlePresenceChange(clientId: ClientId, state: ClientConnectionState): Promise<void> {
const presenceRef = this.squid.collection('presence').doc(clientId);

if (state === 'DISCONNECTED') {
await presenceRef.update({ status: 'offline' });
} else if (state === 'REMOVED') {
await presenceRef.delete();
}
}
}

Frontend: online user を query する

Client code
// Get all online users
const onlineUsers = await squid
.collection<PresenceData>('presence')
.query()
.eq('status', 'online')
.dereference()
.snapshot();

console.log(
'Online users:',
onlineUsers.map((u) => u.userId)
);

query の詳細については、Queriesを参照してください。

関連項目​

  • Authentication - backend でユーザーを識別する
  • Triggers - database change に対応する
  • Database - collection を query および mutate する