Events
信頼された backend code から、1 つ以上の backend handlers に event を broadcast します。
Events を使う理由
1 つの backend action で、複数の独立した後続処理をトリガーする必要があることはよくあります。たとえば新規 signup では、welcome email の送信、resources の provision、analytics の更新が必要になる場合があります。これらすべてを signup function に組み込むと、関係のない関心事が密結合になり、それぞれの変更が難しくなります。
Events を使うと、action は 1 つの event を emit し、それぞれの関心事が独立して subscribe できます。
// Emit once from the code that owns the action.
await this.squid.events().emit({ id: generateUUID(), type: 'user.signed-up', payload: { userId } });
// Handle it in as many independent subscribers as you need.
@eventHandler<{ userId: string }>('user.signed-up')
async sendWelcomeEmail(event: TriggerEvent<{ userId: string }>): Promise<void> {
await this.emailWelcome(event.payload.userId);
}
概要
Events は、Squid backend 内の軽量な publish/subscribe mechanism です。信頼された server-side code が squid.events().emit() で typed event を emit し、その event type に対して @eventHandler で decorate されたすべての backend method が copy を受け取ります。Events は Squid core の internal event stream を通って流れるため、queue connector を設定する必要はありません。
Events を使うべき場合
| Use Case | Recommendation |
|---|---|
| 1 つの server-side action を複数の独立した handlers に展開する | ✅ Events |
| queue topic に publish された messages を処理する(client からのものを含む) | Queue message handlers を使用 |
| database changes に反応する | Triggers を使用 |
| client から function を呼び出す | Executables を使用 |
| schedule に従って code を実行する | Schedulers を使用 |
仕組み
- 信頼された backend code が、
TriggerEvent({ id, type, payload }object)を指定してsquid.events().emit()を呼び出します。 - Squid は core の internal event stream に event を enqueue します。
emit()は、event が handled された時点ではなく、enqueue された時点で resolve します。 - 同じ
typeに対して@eventHandler(type)で decorate されたすべての method が event を受け取ります。 - Delivery は at-least-once で、ordering guarantee はありません。そのため、handler がまれに複数回実行されることがあり、同じ type の events が順不同で到着することがあります。
Events と queue message handlers の違い
どちらも backend handlers に messages を deliver しますが、解決する問題が異なります。
- Events は、ある type に対するすべての
@eventHandlerに broadcast され、Squid の internal event stream 上で動作します。queue connector の設定は不要です。Emit には API key authentication が必要なため、events は常に信頼された backend code から発生します。 - Queue message handlers は、named queue topic(built-in queue または Kafka などの external connector)から messages を consume し、client から produce することもできます。
Quick Start
Prerequisites
squid initで初期化された Squid backend project- NPM から install された
@squidcloud/backendpackage
Step 1: event handler を宣言する
SquidService を extends する service class を作成し、method を @eventHandler で decorate します。
import { SquidService, eventHandler, TriggerEvent } from '@squidcloud/backend';
interface OrderPlaced {
orderId: string;
customerEmail: string;
}
export class ExampleService extends SquidService {
// Runs once for every 'order.placed' event that is emitted.
@eventHandler<OrderPlaced>('order.placed')
async onOrderPlaced(event: TriggerEvent<OrderPlaced>): Promise<void> {
const { orderId, customerEmail } = event.payload;
// Email the customer using your own email helper.
await this.sendOrderConfirmationEmail(customerEmail, orderId);
}
}
Step 2: 信頼された backend code から event を emit する
Emit には API key authentication が必要です。そのため、API key が漏洩してしまう client 上では決して実行せず、backend code で実行する必要があります。this.squid を使って backend Squid instance にアクセスし、events().emit() を呼び出します。
import { SquidService, executable, TriggerEvent } from '@squidcloud/backend';
import { generateUUID } from '@squidcloud/client';
export class ExampleService extends SquidService {
@executable()
async placeOrder(orderId: string, customerEmail: string): Promise<void> {
// Persist the order first, then announce it.
const event: TriggerEvent<OrderPlaced> = {
id: generateUUID(), // Unique per event; used to trace and de-duplicate.
type: 'order.placed',
payload: { orderId, customerEmail },
};
await this.squid.events().emit(event);
}
}
Step 3: backend を start または deploy する
local development では、Squid CLI を使って backend を local で実行します。
squid start
cloud に deploy するには、backend の deploy を参照してください。
Step 4: 検証する
emit をトリガーし(たとえば上記の executable を呼び出す)、Squid Console logs を確認して handler が invoked されたことを確認します。
Core Concepts
TriggerEvent object
すべての event は、3 つの fields を持つ TriggerEvent です。
| Property | Type | Description |
|---|---|---|
id | string | 一意の event id。Handlers は、redelivered events の de-duplicate にこれを使用できます。 |
type | string | event type。どの @eventHandler subscribers が受け取るかを決定します。 |
payload | T | 任意の JSON-serializable event payload。 |
@eventHandler decorator
@eventHandler<T>(type) は、SquidService method を指定された type の events に対する subscriber として mark します。method は完全な TriggerEvent<T> を受け取ります。同じ service 内または異なる services 内の複数の methods が同じ type に subscribe でき、それぞれが独自の copy を受け取ります。
| Parameter | Type | Required | Description |
|---|---|---|---|
type | string | Yes | subscribe する event type。 |
Events を emit する
squid.events().emit(event) は TriggerEvent を publish します。API key authentication が必要なため、backend code 内でのみ実行されます。返される promise は、event が subscribers によって processed された時点ではなく、enqueue された時点で resolve します。
Delivery semantics
Events は at-least-once で deliver され、ordering guarantee はありません。handler が同じ event を複数回受け取ることがあり、同じ type の events が順不同で到着することがあります。Handlers は idempotent かつ order-independent になるように設計してください。
Error Handling
event handler が throw した場合、error は Squid Console に log されます。Delivery は at-least-once であるため、event が redeliver されることがあります。handler logic を try/catch で wrap し、retries が安全になるようにしてください。
@eventHandler<OrderPlaced>('order.placed')
async onOrderPlaced(event: TriggerEvent<OrderPlaced>): Promise<void> {
try {
await this.processOrder(event.payload);
} catch (error) {
// event.id identifies the specific event across redeliveries.
console.error(`Failed to handle order event ${event.id}:`, error);
throw error;
}
}
Best Practices
-
idempotency を考慮して handlers を設計する。 Delivery は at-least-once であるため、handler は同じ event に対して複数回実行される可能性があります。
event.idを使って、すでに完了した処理を検出し、skip してください。 -
backend code からのみ emit する。
emit()には API key が必要です。そのため、key が漏洩してしまう client ではなく、executables または他の backend handlers からトリガーしてください。 -
payloads は小さく、JSON-serializable に保つ。 payload に大きな objects を埋め込むのではなく、identifiers を送信し、subscribers が必要なものを load できるようにしてください。
-
event types に namespace を付ける。
order.placedやuser.signed-upのような names にすると、subscribers の数が増えても types が明確になります。
See Also
- Queue message handlers - queue topic から messages を consume する
- Triggers - database changes に反応する
- Executables - client から backend functions を呼び出す
- Schedulers - schedule に従って code を実行する