Triggers
database の変更に応じて backend function を自動的に実行します。
Triggers を使用する理由
data の変更に対応する必要があります。たとえば、新しい user の signup 時に notification を送信する、product が変更されたときに search index を更新する、record が削除されたときに audit trail を log に記録する、といった場合です。
trigger を使用しない場合、この logic を data を書き込むすべての場所に分散させるか、変更を database で polling する必要があります。trigger を使用すると、監視する collection を宣言するだけで、Squid が function を自動的に呼び出します。
// Backend: runs automatically when a document changes
@trigger('onNewOrder', 'orders')
async handleNewOrder(request: TriggerRequest<Order>): Promise<void> {
if (request.mutationType === 'insert') {
await this.sendOrderConfirmation(request.docAfter);
}
}
polling は不要です。duplicated logic も不要です。data が変更されたときにのみ、code が正確に実行されます。
概要
Triggers は、database collection 内の document が insert、update、delete されたときに自動的に実行される backend function です。mutation の commit 後に実行されるため、reactive workflow を構築する信頼性の高い方法を提供します。
Triggers を使用する場合
| ユースケース | 推奨 |
|---|---|
| database の変更に自動的に対応する | ✅ Trigger |
| client から function を呼び出す | Executables を使用 |
| schedule に従って code を実行する | Schedulers を使用 |
| external service に HTTP endpoint を公開する | Webhooks を使用 |
仕組み
SquidServiceを拡張する class の method を@triggerで decorate します- Squid が deploy 時に trigger を登録します
- 指定 collection 内の document が insert、update、delete されると、Squid が function を呼び出します
- function は mutation type、変更前後の document、document ID を持つ
TriggerRequestを受け取ります
クイックスタート
前提条件
squid initで初期化された Squid backend project- NPM からインストールされた
@squidcloud/backendpackage
ステップ 1: trigger function を作成する
SquidService を拡張する service class を作成し、trigger を追加します。
import { SquidService, trigger, TriggerRequest } from '@squidcloud/backend';
export class ExampleService extends SquidService {
@trigger('userChange', 'users')
async handleUserChange(request: TriggerRequest): Promise<void> {
console.log(`User ${request.docId} was ${request.mutationType}d`);
console.log('Before:', request.docBefore);
console.log('After:', request.docAfter);
}
}
ステップ 2: service を export する
service が service index file から export されていることを確認します。
export * from './example-service';
ステップ 3: backend を開始または deploy する
ローカル開発では、Squid CLI を使用して backend をローカルで実行します。
squid start
cloud に deploy するには、backend の deployを参照してください。
これで、users collection 内の document が insert、update、delete されるたびに trigger が自動的に発火します。
コアコンセプト
@trigger decorator
@trigger decorator には、positional parameter と options object の 2 つの形式があります。
Positional parameter:
@trigger('userChange', 'users') // id, collection (uses built-in DB)
@trigger('userChange', 'users', 'myDatabase') // id, collection, connectorId
| Parameter | Type | 説明 |
|---|---|---|
id | string | この trigger の unique identifier |
collectionName | string | 監視する collection の name |
integrationId? | string | connector ID。default は built-in database です |
Options object:
@trigger({ collection: 'users', mutationTypes: ['insert', 'update'] })
| Property | Type | 説明 |
|---|---|---|
id? | string | unique identifier。省略した場合、default は ClassName.FunctionName |
collection | string | 監視する collection の name |
integrationId? | string | connector ID。default は built-in database |
mutationTypes? | MutationType[] | function を trigger する mutation type を filter します。省略時はすべての mutation が trigger されます |
TriggerRequest
trigger function に渡される TriggerRequest<T> object は、変更に関する detail を提供します。
| Property | Type | 説明 |
|---|---|---|
docId | string | Record<string, any> | document ID(single-field key では string、composite key では object) |
collectionName | string | 影響を受けた collection の name |
integrationId | string | 影響を受けた database の connector ID |
mutationType | MutationType | mutation の type: 'insert'、'update'、または 'delete' |
docBefore? | T | mutation 前の document state。update と delete で利用可能 |
docAfter? | T | mutation 後の document state。insert と update で利用可能 |
generic type parameter T を使用すると、document data に type を付けられます。
interface User {
id: string;
name: string;
email: string;
}
@trigger('userChange', 'users')
async handleUserChange(request: TriggerRequest<User>): Promise<void> {
// request.docAfter is typed as User | undefined
const user = request.docAfter;
if (user) {
console.log(user.name); // type-safe access
}
}
Mutation type
Trigger は 3 種類の mutation に対応します。
| Mutation Type | docBefore | docAfter | 説明 |
|---|---|---|---|
'insert' | undefined | 存在する | 新しい document が作成された |
'update' | 存在する | 存在する | 既存 document が変更された |
'delete' | 存在する | undefined | document が削除された |
Mutation type による Filtering
options object 形式を使用すると、特定の mutation type に対してのみ trigger できます。insert、update、delete ごとに別々の logic が必要な場合に役立ちます。
import { SquidService, trigger, TriggerRequest } from '@squidcloud/backend';
export class OrderService extends SquidService {
@trigger({ collection: 'orders', mutationTypes: ['insert'] })
async onNewOrder(request: TriggerRequest): Promise<void> {
// Only runs on insert
await this.sendOrderConfirmation(request.docAfter);
}
@trigger({ collection: 'orders', mutationTypes: ['update'] })
async onOrderUpdate(request: TriggerRequest): Promise<void> {
// Only runs on update
await this.notifyOrderStatusChange(request.docBefore, request.docAfter);
}
@trigger({ collection: 'orders', mutationTypes: ['delete'] })
async onOrderCancelled(request: TriggerRequest): Promise<void> {
// Only runs on delete
await this.processRefund(request.docBefore);
}
}
External database connector の使用
default では、trigger は built-in database を監視します。external database connector 内の collection を監視するには、connector ID を指定します。
// Positional form
@trigger('userSync', 'users', 'myPostgresDb')
// Options object form
@trigger({ collection: 'users', integrationId: 'myPostgresDb' })
Trigger は、PostgreSQL、MySQL、MongoDB など mutation をサポートする任意の database connector で機能します。
Squid client の使用
this.squid を使用して trigger 内から他の Squid service に access します。これにより、client SDK で利用できる同じ Database operation に access できます。
@trigger('auditLog', 'orders')
async logOrderChange(request: TriggerRequest): Promise<void> {
const auditCollection = this.squid.collection('audit-log');
await auditCollection.doc().insert({
collection: request.collectionName,
docId: request.docId,
mutationType: request.mutationType,
timestamp: new Date(),
before: request.docBefore,
after: request.docAfter,
});
}
Error Handling
Trigger は mutation の commit 後に実行されます。trigger が error を throw しても、元の mutation は rollback されません。変更に関する情報を失わないために、trigger では error を適切に処理する必要があります。
@trigger('processChange', 'payments')
async handlePaymentChange(request: TriggerRequest): Promise<void> {
try {
await this.processPaymentUpdate(request);
} catch (error) {
console.error(`Trigger failed for doc ${request.docId}:`, error);
// Log the failure for manual review
await this.squid.collection('failed-triggers').doc().insert({
docId: request.docId,
mutationType: request.mutationType,
error: String(error),
timestamp: new Date(),
});
}
}
ベストプラクティス
-
trigger を高速に保つ。 trigger は各 mutation 後に asynchronous に実行されます。long-running operation は subsequent change の processing を遅延させます。heavy work は、trigger で queue collection に書き込み、別途処理してください。
-
error を適切に処理する。 trigger error は元の mutation を rollback しないため、failure を log に記録し、trigger された action が重要な場合は retry mechanism を検討してください。
-
mutation type filtering を使用する。 logic が特定の operation にのみ適用される場合は、不要な trigger execution を避けるために
mutationTypesoption を使用します。 -
circular trigger を避ける。 trigger が監視対象と同じ collection に書き込むと、loop 内で自身を trigger します。別の collection に書き込むか、mutation type filtering を使用して cycle を防止してください。
-
TriggerRequest に type を付ける。 document data に type-safe に access するには、generic parameter(
TriggerRequest<MyType>)を使用します。
コード例
新規 record で notification を送信する
import { SquidService, trigger, TriggerRequest } from '@squidcloud/backend';
interface User {
id: string;
name: string;
email: string;
}
export class NotificationService extends SquidService {
@trigger({ collection: 'users', mutationTypes: ['insert'] })
async welcomeNewUser(request: TriggerRequest<User>): Promise<void> {
const user = request.docAfter;
if (!user) return;
await this.squid
.collection('notifications')
.doc()
.insert({
userId: user.id,
message: `Welcome, ${user.name}!`,
createdAt: new Date(),
});
}
}
Derived collection を維持する
import { SquidService, trigger, TriggerRequest } from '@squidcloud/backend';
interface Product {
id: string;
name: string;
price: number;
category: string;
}
export class AnalyticsService extends SquidService {
@trigger('productSync', 'products')
async syncProductStats(request: TriggerRequest<Product>): Promise<void> {
const statsCollection = this.squid.collection('category-stats');
if (request.mutationType === 'insert') {
const product = request.docAfter!;
const statsDoc = statsCollection.doc(product.category);
const stats = await statsDoc.snapshot();
await statsDoc.insert({
category: product.category,
count: (stats?.count ?? 0) + 1,
});
}
if (request.mutationType === 'delete') {
const product = request.docBefore!;
const statsDoc = statsCollection.doc(product.category);
const stats = await statsDoc.snapshot();
if (stats) {
await statsDoc.update({ count: Math.max(0, stats.count - 1) });
}
}
}
}
関連項目
- Executables - client から backend function を呼び出す
- Schedulers - schedule に従って code を実行する
- Webhooks - HTTP endpoint を公開する
- Rate and quota limiting - backend function を保護する
- API reference - trigger decorator の完全な API documentation