トリガー
データベースの変更に応じてバックエンド関数を自動的に実行します。
トリガーを使う理由
データが変更されたときに反応する必要があります。たとえば、新しいユーザーがサインアップしたら通知を送る、商品が変更されたら検索インデックスを更新する、レコードが削除されたら監査ログ(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 エンドポイントを公開したい | Webhooks を使用 |
仕組み
SquidServiceを継承するクラスのメソッドに@triggerをデコレートします- Squid はデプロイ時にトリガーを登録します
- 指定したコレクション内のドキュメントが insert / update / delete されると、Squid が関数を呼び出します
- 関数は
TriggerRequestを受け取り、mutation の種類、変更前後のドキュメント、ドキュメント ID を参照できます
クイックスタート
前提条件
squid initで初期化した Squid backend プロジェクト@squidcloud/backendパッケージがNPMからインストール済み
ステップ 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);
}
}
ステップ 2: service を export する
service の index ファイルから export されていることを確認します。
export * from './example-service';
ステップ 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 の種類: '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 でのフィルタリング
options object 形式を使うと、特定の mutation type のみで発火させられます。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);
}
}
外部 database connector を使う
デフォルトでは、トリガーは built-in database を監視します。外部 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 サービスへアクセスできます。これは 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 がコミットされた後に実行されます。トリガーがエラーを投げても、元の 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 エンドポイントを公開する
- Rate and quota limiting - バックエンド関数を保護する
- API reference - trigger デコレーターの完全な API ドキュメント