Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/tailor-platform/sdk/llms.txt

Use this file to discover all available pages before exploring further.

Resolver triggers fire when a GraphQL resolver completes execution, allowing you to perform follow-up actions like sending notifications, logging events, or triggering workflows based on resolver results.

Overview

The resolverExecutedTrigger() function creates triggers that fire after a resolver executes. You can access both successful results and error information, making it ideal for post-processing, auditing, and event-driven workflows.

Parameters

resolver
ResolverConfig
required
The resolver to monitor for execution events. Must be a resolver created with createResolver().
condition
function
Optional filter function that receives the execution result and returns a boolean. Only triggers if the function returns true.Parameters:
  • success - Boolean indicating if resolver succeeded
  • result - Resolver output (only available when success is true)
  • error - Error message (only available when success is false)
  • resolverName - Name of the executed resolver
  • workspaceId - Workspace identifier
  • appNamespace - Application namespace

Basic Example

import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
import { createOrderResolver } from "../resolvers/create-order";

export default createExecutor({
  name: "order-created-notification",
  description: "Send notification after order is created",
  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}`);
      // Send notification
    },
  },
});

Event Context

Resolver triggers receive a discriminated union based on execution success:
type ResolverExecutedArgs<R> = EventArgs & {
  resolverName: string;
} & (
  | {
      success: true;
      result: ResolverOutput<R>;
      error?: never;
    }
  | {
      success: false;
      result?: never;
      error: string;
    }
);

Accessing Results Safely

Use the success field to narrow the type:
body: async (args) => {
  if (args.success) {
    // TypeScript knows `result` is available
    console.log("Result:", args.result);
  } else {
    // TypeScript knows `error` is available
    console.error("Error:", args.error);
  }
}

Legacy Condition Pattern

You can also check for error in the condition:
trigger: resolverExecutedTrigger({
  resolver: myResolver,
  condition: ({ result, error }) => {
    // Fires only on success with valid result
    return !error && !!result?.data;
  },
})

Real-World Examples

Post-Processing with Webhook

import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
import stepChain from "../resolvers/stepChain";

export default createExecutor({
  name: "step-chain-executed",
  description: "Triggered when a step chain is executed",
  trigger: resolverExecutedTrigger({
    resolver: stepChain,
    condition: ({ result }) => {
      if (!result) return false;
      return result.result.summary.length > 0;
    },
  }),
  operation: {
    kind: "webhook",
    url: ({ result }) => 
      `https://example.com/webhook/${result!.result.summary.length}`,
    headers: {
      "Content-Type": "application/json",
      Authorization: { vault: "my-vault", key: "my-secret" },
    },
    requestBody: ({ result }) => ({
      orderId: result!.result.summary[0],
      customerID: result!.result.summary[1],
      totalPrice: result!.result.summary[2],
    }),
  },
});

Error Logging and Monitoring

import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
import { criticalResolver } from "../resolvers/critical";

export default createExecutor({
  name: "resolver-error-monitor",
  description: "Monitor and log resolver errors",
  trigger: resolverExecutedTrigger({
    resolver: criticalResolver,
    // Trigger only on errors
    condition: ({ success }) => !success,
  }),
  operation: {
    kind: "function",
    body: async (args) => {
      if (!args.success) {
        console.error({
          resolver: args.resolverName,
          error: args.error,
          timestamp: new Date().toISOString(),
          workspace: args.workspaceId,
        });
        
        // Send alert to monitoring service
      }
    },
  },
});

Audit Trail

import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
import { updateUserResolver } from "../resolvers/update-user";
import { getDB } from "../generated/tailordb";

export default createExecutor({
  name: "user-update-audit",
  description: "Log all user updates for audit purposes",
  trigger: resolverExecutedTrigger({
    resolver: updateUserResolver,
  }),
  operation: {
    kind: "function",
    body: async (args) => {
      const db = getDB("tailordb");
      
      await db
        .insertInto("AuditLog")
        .values({
          action: "resolver_executed",
          resolverName: args.resolverName,
          success: args.success,
          result: args.success ? JSON.stringify(args.result) : null,
          error: args.success ? null : args.error,
          timestamp: new Date(),
        })
        .execute();
    },
  },
});

Trigger Workflow on Success

import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
import { createPaymentResolver } from "../resolvers/create-payment";
import fulfillmentWorkflow from "../workflows/fulfillment";

export default createExecutor({
  name: "payment-to-fulfillment",
  description: "Start fulfillment workflow after successful payment",
  trigger: resolverExecutedTrigger({
    resolver: createPaymentResolver,
    condition: ({ success, result }) => 
      success && result?.payment?.status === "succeeded",
  }),
  operation: {
    kind: "workflow",
    workflow: fulfillmentWorkflow,
    args: ({ result }) => ({
      orderId: result!.payment.orderId,
      paymentId: result!.payment.id,
    }),
  },
});

GraphQL Mutation on Resolver Success

import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
import { processOrderResolver } from "../resolvers/process-order";

export default createExecutor({
  name: "order-processed-notification",
  description: "Create notification record after order processing",
  trigger: resolverExecutedTrigger({
    resolver: processOrderResolver,
    condition: ({ success }) => success,
  }),
  operation: {
    kind: "graphql",
    query: `
      mutation CreateNotification($input: NotificationCreateInput!) {
        createNotification(input: $input) {
          id
          message
        }
      }
    `,
    variables: ({ result }) => ({
      input: {
        userId: result!.order.customerId,
        message: `Your order ${result!.order.id} has been processed`,
        type: "ORDER_UPDATE",
      },
    }),
  },
});

Use Cases

Post-Processing

Perform additional operations after a resolver completes:
  • Send confirmation emails
  • Update related records
  • Trigger external webhooks
  • Generate reports

Monitoring and Observability

Track resolver performance and errors:
  • Log execution metrics
  • Send alerts on failures
  • Track success rates
  • Monitor response times

Event-Driven Workflows

Chain operations based on resolver outcomes:
  • Start multi-step workflows
  • Trigger downstream services
  • Coordinate microservices
  • Implement saga patterns

Audit and Compliance

Maintain audit trails:
  • Log all resolver executions
  • Track data changes
  • Record user actions
  • Meet compliance requirements

Advanced Patterns

Conditional Branching

export default createExecutor({
  name: "resolver-branch",
  trigger: resolverExecutedTrigger({
    resolver: myResolver,
  }),
  operation: {
    kind: "function",
    body: async (args) => {
      if (!args.success) {
        // Handle error case
        await handleError(args.error);
        return;
      }
      
      // Handle success case
      if (args.result.amount > 1000) {
        await processLargeOrder(args.result);
      } else {
        await processSmallOrder(args.result);
      }
    },
  },
});

Retry Failed Resolvers

export default createExecutor({
  name: "resolver-retry",
  trigger: resolverExecutedTrigger({
    resolver: unreliableResolver,
    condition: ({ success }) => !success,
  }),
  operation: {
    kind: "function",
    body: async ({ error, resolverName }) => {
      console.log(`${resolverName} failed: ${error}`);
      // Implement retry logic or dead letter queue
    },
  },
});

Fan-Out Pattern

export default createExecutor({
  name: "resolver-fanout",
  trigger: resolverExecutedTrigger({
    resolver: dataResolver,
    condition: ({ success }) => success,
  }),
  operation: {
    kind: "function",
    body: async ({ result }) => {
      // Trigger multiple downstream operations
      await Promise.all([
        notifyUsers(result),
        updateAnalytics(result),
        syncToWarehouse(result),
      ]);
    },
  },
});

Best Practices

Always Check Success Status

body: async (args) => {
  if (args.success) {
    // Safe to access args.result
  } else {
    // Safe to access args.error
  }
}

Use Condition Functions

Filter events at the trigger level to reduce unnecessary executions:
trigger: resolverExecutedTrigger({
  resolver: myResolver,
  condition: ({ success, result }) => 
    success && result?.status === "completed",
})

Handle Both Success and Error Cases

body: async (args) => {
  const db = getDB("tailordb");
  
  await db
    .insertInto("ResolverLog")
    .values({
      resolverName: args.resolverName,
      success: args.success,
      data: args.success ? args.result : null,
      error: args.success ? null : args.error,
    })
    .execute();
}

Type Safety

The result type is automatically inferred from your resolver:
import { createResolver } from "@tailor-platform/sdk";

const myResolver = createResolver({
  name: "myResolver",
  output: t.object({ orderId: t.string(), total: t.number() }),
  // ...
});

// TypeScript knows the result type
trigger: resolverExecutedTrigger({
  resolver: myResolver,
  condition: ({ result }) => {
    // result.orderId and result.total are typed!
    return result?.total > 100;
  },
})
Resolver triggers execute asynchronously after the resolver completes. They cannot modify the resolver’s response or prevent it from returning.

Build docs developers (and LLMs) love