トリガー
データベースの変更に応じてバックエンド関数を自動的に実行します。
トリガーを使う理由
データが変更されたときに反応する必要があります。新規ユーザー登録時に通知を送信したり、商品が変更されたら検索インデックスを更新したり、レコードが削除されたら監査ログ(audit trail)を記録したり、などです。
トリガーがない場合、このロジックをデータを書き込むあらゆる場所に分散して書くか、データベースの変更をポーリングして検知する必要があります。トリガーを使えば、監視するコレクションを宣言するだけで、Squid があなたの関数を自動的に呼び出します。
// 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);
}
}
ポーリング不要。ロジックの重複なし。データが変更されたまさにそのタイミングでコードが実行されます。
概要
トリガーは、データベースコレクション内のドキュメントが insert / update / delete されたときに自動的に実行されるバックエンド関数です。mutation(変更)がコミットされた後に実行されるため、リアクティブなワークフローを構築するための信頼できる手段になります。
トリガーを使うべきとき
| ユースケース | 推奨 |
|---|---|
| データベース変更に自動で反応する | ✅ Trigger |
| クライアントから関数を呼び出す | Executables を使用 |
| スケジュールでコードを実行する | Schedulers を使用 |
| 外部サービス向けに HTTP endpoint を公開する | Webhooks を使用 |
仕組み
SquidServiceを継承したクラス内のメソッドに@triggerを付与する- Squid が deploy 時にトリガーを登録する
- 指定されたコレクションのドキュメントが insert / update / delete されると、Squid がその関数を呼び出す
- 関数は
TriggerRequestを受け取り、mutation type、変更前後のドキュメント、ドキュメント ID が渡される
クイックスタート
前提条件
squid init-backendで初期化済みの Squid backend project@squidcloud/backendパッケージがインストールされていること
Step 1: トリガー関数を作成する
SquidService を継承する service クラスを作成し、トリガーを追加します。
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);
}
}
Step 2: service をエクスポートする
service の index ファイルからエクスポートされていることを確認します。
export * from './example-service';
Step 3: バックエンドを起動またはデプロイする
ローカル開発では、Squid CLI を使ってバックエンドをローカル実行します。
squid start
クラウドにデプロイするには、deploying your backend を参照してください。
これで users コレクションのドキュメントが insert / update / delete されるたびに、トリガーが自動的に発火します。
コアコンセプト
@trigger デコレーター
@trigger デコレーターには 2 つの形式があります: 位置引数(positional parameters)と options object です。
位置引数(positional parameters):
@trigger('userChange', 'users') // id, collection (uses built-in DB)
@trigger('userChange', 'users', 'myDatabase') // id, collection, connectorId
| パラメータ | 型 | 説明 |
|---|---|---|
id | string | このトリガーの一意な識別子 |
collectionName | string | 監視するコレクション名 |
integrationId? | string | connector ID。デフォルトは built-in database |
Options object:
@trigger({ collection: 'users', mutationTypes: ['insert', 'update'] })
| プロパティ | 型 | 説明 |
|---|---|---|
id? | string | 一意な識別子。省略時は ClassName.FunctionName がデフォルト |
collection | string | 監視するコレクション名 |
integrationId? | string | connector ID。デフォルトは built-in database |
mutationTypes? | MutationType[] | どの mutation type で関数を起動するかをフィルタリング。省略時はすべての mutation で起動 |
TriggerRequest
トリガー関数に渡される TriggerRequest<T> オブジェクトには、変更に関する詳細が含まれます。
| プロパティ | 型 | 説明 |
|---|---|---|
docId | string | Record<string, any> | ドキュメント ID(単一フィールドのキーは string、複合キーは object) |
collectionName | string | 影響を受けたコレクション名 |
integrationId | string | 影響を受けたデータベースの connector ID |
mutationType | MutationType | mutation type: 'insert'、'update'、'delete' |
docBefore? | T | mutation 前のドキュメント状態。update と delete で利用可能 |
docAfter? | T | mutation 後のドキュメント状態。insert と update で利用可能 |
ジェネリック型パラメータ T により、ドキュメントデータを型付けできます。
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 types
トリガーは 3 種類の mutation に反応します。
| Mutation Type | docBefore | docAfter | 説明 |
|---|---|---|---|
'insert' | undefined | Present | 新しいドキュメントが作成された |
'update' | Present | Present | 既存のドキュメントが変更された |
'delete' | Present | undefined | ドキュメントが削除された |
mutation type によるフィルタリング
特定の mutation type のみで発火させたい場合は、options object 形式を使います。insert / update / delete で別々のロジックが必要なときに便利です。
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);
}
}
外部データベース connector を使う
デフォルトでは、トリガーは built-in database を監視します。外部データベース connector 内のコレクションを監視するには、connector ID を指定してください。
// Positional form
@trigger('userSync', 'users', 'myPostgresDb')
// Options object form
@trigger({ collection: 'users', integrationId: 'myPostgresDb' })
トリガーは、PostgreSQL、MySQL、MongoDB など、mutation をサポートする任意の database connector で動作します。
Squid client を使う
トリガー内から this.squid を使って他の Squid services にアクセスできます。これにより、client SDK で利用できるのと同じ Database 操作が使えます。
@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,
});
}
エラーハンドリング
トリガーは mutation のコミット後に実行されます。トリガーがエラーを throw しても、元の mutation はロールバックされません。変更情報を失わないように、トリガー側で適切にエラーを処理してください。
@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(),
});
}
}
ベストプラクティス
-
トリガーは高速に保つ。 トリガーは各 mutation の後に非同期で実行されます。時間のかかる処理は後続の変更の処理を遅らせます。重い処理は、トリガーで queue 用のコレクションに書き込み、別途それを処理してください。
-
エラーを丁寧に処理する。 トリガーのエラーは元の mutation をロールバックしないため、失敗をログに残し、重要な処理であればリトライ機構を検討してください。
-
mutation type フィルタリングを使う。 特定の操作にのみロジックを適用したい場合は
mutationTypesオプションを使い、不要なトリガー実行を避けてください。 -
循環トリガーを避ける。 トリガーが監視対象と同じコレクションに書き込むと、ループして自分自身を発火させます。別コレクションに書き込むか、mutation type フィルタリングでサイクルを防いでください。
-
TriggerRequest を型付けする。 ドキュメントデータへの型安全なアクセスのために、ジェネリックパラメータ(
TriggerRequest<MyType>)を使ってください。
コード例
新規レコード作成時に通知を送る
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 - クライアントからバックエンド関数を呼び出す
- Schedulers - スケジュールに従ってコードを実行する
- Webhooks - HTTP endpoints を公開する
- Rate and quota limiting - バックエンド関数を保護する
- API reference - trigger デコレーターの完全な API ドキュメント