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

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 できます。

Backend code
// Emit once from the code that owns the action.
await this.squid.events().emit({ id: generateUUID(), type: 'user.signed-up', payload: { userId } });
Backend code
// 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 CaseRecommendation
1 つの server-side action を複数の独立した handlers に展開する✅ Events
queue topic に publish された messages を処理する(client からのものを含む)Queue message handlers を使用
database changes に反応するTriggers を使用
client から function を呼び出すExecutables を使用
schedule に従って code を実行するSchedulers を使用

仕組み

  1. 信頼された backend code が、TriggerEvent{ id, type, payload } object)を指定して squid.events().emit() を呼び出します。
  2. Squid は core の internal event stream に event を enqueue します。emit() は、event が handled された時点ではなく、enqueue された時点で resolve します。
  3. 同じ type に対して @eventHandler(type) で decorate されたすべての method が event を受け取ります。
  4. 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/backend package

Step 1: event handler を宣言する

SquidService を extends する service class を作成し、method を @eventHandler で decorate します。

Backend code
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() を呼び出します。

Backend code
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 です。

PropertyTypeDescription
idstring一意の event id。Handlers は、redelivered events の de-duplicate にこれを使用できます。
typestringevent type。どの @eventHandler subscribers が受け取るかを決定します。
payloadT任意の JSON-serializable event payload。

@eventHandler decorator

@eventHandler<T>(type) は、SquidService method を指定された type の events に対する subscriber として mark します。method は完全な TriggerEvent<T> を受け取ります。同じ service 内または異なる services 内の複数の methods が同じ type に subscribe でき、それぞれが独自の copy を受け取ります。

ParameterTypeRequiredDescription
typestringYessubscribe する 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 が安全になるようにしてください。

Backend code
@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

  1. idempotency を考慮して handlers を設計する。 Delivery は at-least-once であるため、handler は同じ event に対して複数回実行される可能性があります。event.id を使って、すでに完了した処理を検出し、skip してください。

  2. backend code からのみ emit する。 emit() には API key が必要です。そのため、key が漏洩してしまう client ではなく、executables または他の backend handlers からトリガーしてください。

  3. payloads は小さく、JSON-serializable に保つ。 payload に大きな objects を埋め込むのではなく、identifiers を送信し、subscribers が必要なものを load できるようにしてください。

  4. event types に namespace を付ける。 order.placeduser.signed-up のような names にすると、subscribers の数が増えても types が明確になります。

See Also