Executor
Executors are event-driven handlers that automatically trigger in response to data changes, schedules, or external events.
Overview
Executors provide:
- Automatic triggers on record changes (create, update, delete)
- Scheduled execution via cron expressions
- Incoming webhook handlers
- Post-resolver execution hooks
- Multiple operation types (functions, webhooks, GraphQL, workflows)
For the official Tailor Platform documentation, see Executor Guide.
Creating an Executor
Define executors in files matching glob patterns specified in tailor.config.ts.
Definition Rules:
- One executor per file: Each file must contain exactly one executor definition
- Export method: Must use
export default - Uniqueness: Executor names must be unique globally across your entire application
import { createExecutor, recordCreatedTrigger, t } from "@tailor-platform/sdk";
import { user } from "../tailordb/user";
export default createExecutor({
name: "user-welcome",
description: "Send welcome email to new users",
trigger: recordCreatedTrigger({
type: user,
condition: ({ newRecord }) => !!newRecord.email && newRecord.isActive,
}),
operation: {
kind: "function",
body: async ({ newRecord }) => {
// Send welcome email logic here
},
},
});Trigger Types
Record Triggers
Fire when records are created, updated, or deleted:
recordCreatedTrigger(): Fires when a new record is createdrecordUpdatedTrigger(): Fires when a record is updatedrecordDeletedTrigger(): Fires when a record is deleted
Each trigger can include an optional filter function:
recordUpdatedTrigger({
type: order,
condition: ({ newRecord, oldRecord }) =>
newRecord.status === "completed" && oldRecord.status !== "completed",
});Schedule Trigger
Fires on a cron schedule:
scheduleTrigger({ cron: "*/5 * * * *" }); // Every 5 minutes
scheduleTrigger({ cron: "0 9 * * 1" }); // Every Monday at 9am
scheduleTrigger({ cron: "0 0 1 * *" }); // First day of every month
scheduleTrigger({ cron: "0 * * * *", timezone: "Asia/Tokyo" });Incoming Webhook Trigger
Fires when an external webhook is received:
type WebhookRequest = {
body: WebhookPayload;
headers: Record<string, string>;
};
incomingWebhookTrigger<WebhookRequest>();Resolver Executed Trigger
Fires when a resolver is executed:
resolverExecutedTrigger({
resolver: createOrderResolver,
condition: ({ result, error }) => !error && result?.order?.id,
});Operation Types
Function Operation
Execute JavaScript/TypeScript functions:
createExecutor({
operation: {
kind: "function",
body: async ({ newRecord }) => {
console.log("New record created:", newRecord);
},
},
});Job Function Operation
For long-running operations, use jobFunction which runs asynchronously and supports extended execution times. See Job Function Operation for details.
import { createExecutor, scheduleTrigger } from "@tailor-platform/sdk";
import { getDB } from "../generated/tailordb";
export default createExecutor({
name: "daily-report-generator",
description: "Generate daily reports",
trigger: scheduleTrigger({ cron: "0 0 * * *" }),
operation: {
kind: "jobFunction",
body: async () => {
const db = getDB("tailordb");
// Long-running report generation logic
const records = await db.selectFrom("Order").selectAll().execute();
// Process records...
},
},
});Webhook Operation
Call external webhooks with dynamic data:
createExecutor({
operation: {
kind: "webhook",
url: ({ typeName }) => `https://api.example.com/webhooks/${typeName}`,
headers: {
"Content-Type": "application/json",
"X-API-Key": { vault: "api-keys", key: "external-api" },
},
requestBody: ({ newRecord }) => ({
id: newRecord.id,
timestamp: new Date(),
data: newRecord,
}),
},
});GraphQL Operation
Execute GraphQL queries and mutations:
createExecutor({
operation: {
kind: "graphql",
appName: "my-app",
query: `
mutation UpdateUserStatus($id: ID!, $status: String!) {
updateUser(id: $id, input: { status: $status }) {
id
status
updatedAt
}
}
`,
variables: ({ newRecord }) => ({
id: newRecord.userId,
status: "active",
}),
},
});Workflow Operation
Trigger workflows from executors. See Workflow documentation for how to define workflows.
import { createExecutor, recordCreatedTrigger } from "@tailor-platform/sdk";
import { order } from "../tailordb/order";
import processOrderWorkflow from "../workflows/process-order";
export default createExecutor({
name: "order-processor",
description: "Process new orders via workflow",
trigger: recordCreatedTrigger({ type: order }),
operation: {
kind: "workflow",
workflow: processOrderWorkflow,
args: ({ newRecord }) => ({
orderId: newRecord.id,
customerId: newRecord.customerId,
}),
},
});You can also pass static arguments:
createExecutor({
operation: {
kind: "workflow",
workflow: dailyReportWorkflow,
args: { reportType: "summary" },
},
});Authentication for Operations
GraphQL and Workflow operations can specify an authInvoker to execute with machine user credentials:
import { defineAuth, createExecutor, scheduleTrigger } from "@tailor-platform/sdk";
const auth = defineAuth("my-auth", {
// ... auth configuration
machineUsers: {
"batch-processor": {
attributes: { role: "ADMIN" },
},
},
});
export default createExecutor({
name: "scheduled-cleanup",
trigger: scheduleTrigger({ cron: "0 0 * * *" }),
operation: {
kind: "graphql",
query: `mutation { cleanupOldRecords { count } }`,
authInvoker: auth.invoker("batch-processor"),
},
});Event Payloads
Each trigger type provides specific context data in the callback functions.
Record Event Payloads
Record triggers receive context based on the operation type:
Created Event
interface RecordCreatedContext<T> {
workspaceId: string; // Workspace identifier
appNamespace: string; // Application/namespace name
typeName: string; // TailorDB type name
newRecord: T; // The newly created record
}Updated Event
interface RecordUpdatedContext<T> {
workspaceId: string;
appNamespace: string;
typeName: string;
oldRecord: T; // Previous record state
newRecord: T; // Current record state
}Deleted Event
interface RecordDeletedContext<T> {
workspaceId: string;
appNamespace: string;
typeName: string;
oldRecord: T; // The deleted record
}Usage Example:
import { createExecutor, recordUpdatedTrigger, t } from "@tailor-platform/sdk";
import { order } from "../tailordb/order";
export default createExecutor({
name: "order-status-changed",
trigger: recordUpdatedTrigger({
type: order,
condition: ({ oldRecord, newRecord }) => oldRecord.status !== newRecord.status,
}),
operation: {
kind: "function",
body: async ({ oldRecord, newRecord, typeName }) => {
console.log(`${typeName} status changed:`);
console.log(` From: ${oldRecord.status}`);
console.log(` To: ${newRecord.status}`);
},
},
});Schedule Event Payload
Schedule triggers receive minimal context:
interface ScheduleContext {
scheduledTime: string; // ISO 8601 timestamp
}Incoming Webhook Payload
Webhook triggers receive HTTP request data:
interface WebhookContext<T = unknown> {
body: T; // Parsed request body
headers: Record<string, string>; // Request headers
method: "POST" | "GET" | "PUT" | "DELETE"; // HTTP method
rawBody: string; // Raw request body as string
}Usage Example:
import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";
interface StripeWebhook {
type: string;
data: { object: { id: string; amount: number } };
}
export default createExecutor({
name: "stripe-webhook",
trigger: incomingWebhookTrigger<{
body: StripeWebhook;
headers: { "stripe-signature": string };
}>(),
operation: {
kind: "function",
body: async ({ body, headers }) => {
const signature = headers["stripe-signature"];
console.log(`Received ${body.type} event`);
// Process webhook...
},
},
});Resolver Executed Payload
Resolver triggers receive the resolver's result or error:
interface ResolverExecutedContext<TResult> {
workspaceId: string; // Workspace identifier
appNamespace: string; // Application/namespace name
resolverName: string; // Name of the executed resolver
result?: TResult; // Return value (on success)
error?: string; // Error message (on failure)
}Usage Example:
import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
import { createOrderResolver } from "../resolvers/create-order";
export default createExecutor({
name: "order-created-notification",
trigger: resolverExecutedTrigger({
resolver: createOrderResolver,
condition: ({ result, error }) => !error && !!result?.order,
}),
operation: {
kind: "function",
body: async ({ result, resolverName }) => {
console.log(`${resolverName} completed successfully`);
console.log(`Order ID: ${result.order.id}`);
},
},
});