Skip to content
View as Markdown

Function service

Overview

Function service lets you run your JavaScript code as serverless functions in Tailor Platform. You can write your code and deploy it to Tailor Platform, and then trigger it from resolvers or Executors. This provides a flexible way to extend your application's functionality with custom business logic.

With Function service, you can:

  • Send HTTP requests to 3rd party applications
  • Access and execute SQL queries to TailorDB
  • Manage IdP users, secrets, files, and workflows via built-in interfaces
  • Process data transformations
  • Implement custom business logic with npm packages

To keep the this guide simple, we'll focus on a basic example of writing a function that returns a greeting message. For more advanced use cases, you can refer to these sections:

How to write and deploy your code in Function service

The deployment process involves three main steps:

  1. Write your function
  2. Set up the function in your Resolver or Executor
  3. Execute the function from your application

You can see the actual code examples here.

1. Writing Your Function

With SDK, functions are defined inline within the resolver or executor files using TypeScript.

2. Set up the function in your resolver or Executor

Directory Structure

Here's the sample directory structure for an SDK project. Your function code is defined inline within the resolver or executor files.

sh
.
├── tailor.config.ts
├── package.json
├── resolvers
   └── function-sample-hello.ts
├── executors
   └── webhook-function.ts
└── tailordb
    └── ...

Resolver Example

With SDK, the schema definition is inferred from the input/output types. Here's a sample SDK configuration for setting up a resolver with a function:

typescript
import { createResolver, t } from "@tailor-platform/sdk";

export default createResolver({
  name: "functionSampleHello",
  description: "query resolver for function sample",
  operation: "query",
  input: {
    message: t.string(),
  },
  body: (context) => {
    return {
      message: context.input.message,
    };
  },
  output: t.object({
    message: t.string().nullable(),
  }),
});

Executor incoming webhook Example

typescript
import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";

export default createExecutor({
  name: "sample-function-01",
  description: "sample function",
  trigger: incomingWebhookTrigger(),
  operation: {
    kind: "function",
    body: async () => {
      return {
        message: "hi " + new Date().toISOString(),
      };
    },
  },
});

Once you've defined the SDK configuration, you can deploy it to Tailor Platform. Please confirm your tailor.config.ts points to your target workspace and run this command:

bash
npx tailor deploy

3. Execute the function from your application

After deploying your function, you can open the graphql playground and run the query.

To open the graphql playground, run the following command:

bash
npx tailor app open -n {APP_NAME}

Then, you can run the query in the playground:

graphql
query {
  functionSampleHello(input: { message: "test" }) {
    message
  }
}

Function operations are subject to a recursive call depth limit of 10 levels when calling other platform services. See Platform Limits for more details.

Using the principal (caller / invoker)

When using the Function service with resolvers or Executors, you can access the current principal — a TailorPrincipal — from the body function. Resolvers expose it as caller; function/jobFunction executor operations expose it as invoker. Both are null for anonymous calls.

TailorPrincipal has the following properties:

  • id: The ID of the principal
  • type: "user" or "machine_user"
  • workspaceId: The ID of the workspace the principal belongs to
  • attributes: A map of the principal's attributes
  • attributeList: A list of the principal's attribute IDs

Resolver Example

In SDK, you can access the current caller's information via context.caller in the body function:

typescript
import { createResolver, t } from "@tailor-platform/sdk";

export default createResolver({
  name: "functionSampleHello",
  description: "query resolver for function sample",
  operation: "query",
  input: {
    message: t.string(),
  },
  body: (context) => {
    return {
      message: context.caller?.id,
    };
  },
  output: t.object({
    message: t.string().nullable(),
  }),
});

Executor Example

typescript
import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";

export default createExecutor({
  name: "sample-function-01",
  description: "sample function",
  trigger: incomingWebhookTrigger(),
  operation: {
    kind: "function",
    body: async (context) => {
      return {
        message: "hi " + context.invoker?.id,
      };
    },
  },
});

Further details

For more advanced use cases, you can refer to these sections:

For list of supported packages, please refer to Appendix.