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 を使用 |
仕組み
- client が Squid に接続し、一意の
clientIdが割り当てられます - backend handler が
CONNECTEDstate で呼び出されます - client が切断した場合(例: browser tab を閉じた場合)、handler が
DISCONNECTEDで呼び出されます - 一定期間後も client が再接続しない場合、Squid は
clientIdを削除し、REMOVEDを指定して handler を呼び出します - 削除後に client が再接続した場合、新しい
clientIdが割り当てられます
クイックスタート
前提条件
squid initで初期化された Squid backend project- NPM からインストールされた
@squidcloud/backendpackage
ステップ 1: Connection handler を作成する
service class に connection state handler を追加します。
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 を監視する
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 | 意味 |
|---|---|
CONNECTED | client が server に接続した直後です |
DISCONNECTED | client は切断されましたが、再接続時に備えて Squid は clientId を保持します |
REMOVED | client は十分長く切断されていたため、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 を受け取ります。
| Parameter | Type | 説明 |
|---|---|---|
clientId | ClientId(string) | state が change した client の ID |
connectionState | ClientConnectionState | 'CONNECTED'、'DISCONNECTED'、'REMOVED' のいずれか |
function は void または Promise<void> を返せます。
Frontend connection API
connection 情報には squid.connectionDetails() を介してアクセスします。
| Property / Method | Return Type | 説明 |
|---|---|---|
.connected | boolean | client が現在接続されているかどうか |
.clientId | string | この 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 のみで通信します。
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 updateconnectionDetails().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 を処理してください。
@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 で初期化されていることを確認します |
ベストプラクティス
- 接続時に
clientIdをuserIdに mapping します。clientIdは connection-scoped、userIdは auth-scoped であるため、client 接続時に mapping を insert すれば、connection から user を lookup できます。 DISCONNECTEDだけでなくREMOVEDで cleanup します。DISCONNECTEDclient は同じclientIdで再接続する可能性があります。resource を削除するのは state がREMOVEDの場合だけにしてください。- handler logic は高速に保ちます。 connection state handler は全 client の接続・切断ごとに実行されます。高コストな operation は避けるか、非同期で chain してください。
- ユーザーの識別には authentication を使用します。
clientIdだけではユーザーが誰かは分かりません。auth と組み合わせて presence feature を構築してください。
コード例
User presence tracking
この例では、presence collection 内で clientId を userId に mapping して、online のユーザーを追跡します。
Frontend: 接続時に presence を登録する
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 を更新する
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 する
// 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 する