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.

The Tailor Platform SDK provides comprehensive TypeScript type safety throughout your application, from database schemas to resolvers, executors, and workflows. Types are inferred automatically, reducing boilerplate while maintaining strict type checking.

Type Builders

The SDK provides type builders through the t namespace for defining inputs and outputs:
import { t } from "@tailor-platform/sdk";

// Primitive types
t.string()
t.int()
t.float()
t.bool()
t.uuid()

// Complex types
t.enum(["MANAGER", "STAFF"])
t.object({
  name: t.string(),
  email: t.string(),
})
t.array(t.string())

Type Modifiers

Add descriptions and validations to enhance type definitions:
resolvers/add.ts
import { createResolver, t } from "@tailor-platform/sdk";

const validators: [(a: { value: number }) => boolean, string][] = [
  [({ value }) => value >= 0, "Value must be non-negative"],
  [({ value }) => value < 10, "Value must be less than 10"],
];

export default createResolver({
  name: "add",
  description: "Addition operation",
  operation: "query",
  input: {
    a: t
      .int()
      .description("First number to add")
      .validate(...validators),
    b: t
      .int()
      .description("Second number to add")
      .validate(...validators),
  },
  body: ({ input }) => input.a + input.b,
  output: t.int().description("Sum of the two input numbers"),
});

Database Type Safety

TailorDB types are defined using the db namespace and automatically provide TypeScript types:
tailordb/user.ts
import { db } from "@tailor-platform/sdk";

export const user = db
  .type("User", {
    name: db.string(),
    email: db.string().unique(),
    status: db.string({ optional: true }),
    department: db.string({ optional: true }),
    role: db.enum(["MANAGER", "STAFF"]),
    ...db.fields.timestamps(),
  })
  .files({ avatar: "profile image" })
  .indexes(
    { fields: ["name", "department"], unique: false },
    { fields: ["status", "createdAt"], unique: false, name: "user_status_created_idx" }
  );

Type Inference with t.infer

Extract TypeScript types from TailorDB definitions:
executors/userRecordLog.ts
import { t } from "@tailor-platform/sdk";
import { getDB } from "../generated/tailordb";
import { user } from "../tailordb/user";

export default async ({ newRecord }: { newRecord: t.infer<typeof user> }) => {
  const db = getDB("tailordb");
  const record = await db
    .selectFrom("User")
    .selectAll()
    .where("id", "=", newRecord.id)
    .executeTakeFirst();

  await db
    .insertInto("UserLog")
    .values({
      userID: newRecord.id,
      message: `User created: ${record?.name} (${record?.email})`,
    })
    .execute();
};
t.infer<typeof user> extracts the TypeScript type from the database schema definition, ensuring type consistency across your application.

Relations and Type Safety

Relations between types are automatically typed:
tailordb/salesOrder.ts
import { db } from "@tailor-platform/sdk";
import { customer } from "./customer";
import { user } from "./user";

export const salesOrder = db
  .type(["SalesOrder", "SalesOrderList"], {
    customerID: db.uuid().relation({
      type: "n-1",
      toward: { type: customer },
    }),
    approvedByUserIDs: db.uuid({ optional: true, array: true }).relation({
      type: "keyOnly",
      toward: { type: user },
    }),
    totalPrice: db.int({ optional: true }),
    discount: db.float({ optional: true }),
    status: db.string({ optional: true }),
    ...db.fields.timestamps(),
  })
  .files({
    receipt: "receipt file",
    form: "order form file",
  });

Resolver Type Safety

Resolvers automatically infer input and output types:
resolvers/userInfo.ts
import { createResolver, t } from "@tailor-platform/sdk";

export default createResolver({
  name: "showUserInfo",
  description: "Show current user information",
  operation: "query",
  body: (context) => {
    return {
      id: context.user.id,
      type: context.user.type,
      workspaceId: context.user.workspaceId,
      role: context.user.attributes?.role ?? "MANAGER",
    };
  },
  output: t
    .object({
      id: t.string().description("User ID"),
      type: t.string().description("User type"),
      workspaceId: t.string().description("Workspace ID"),
      role: t.enum(["MANAGER", "STAFF"]).description("User role"),
    })
    .description("User information"),
});

Context Type Safety

The resolver context is fully typed:
body: ({ input, env, user, db }) => {
  // input: inferred from input definition
  // env: inferred from config env variables
  // user: { id: string; type: string; workspaceId: string; attributes?: Record<string, any> }
  // db: getDB() for database access
}

Executor Type Safety

Executors enforce type safety for triggers and operations:
executors/userCreated.ts
import { createExecutor, recordCreatedTrigger } from "@tailor-platform/sdk";
import { user } from "../tailordb/user";
import userRecordLog from "./userRecordLog";

export default createExecutor({
  name: "user-created",
  description: "Triggered when a new user is created",
  trigger: recordCreatedTrigger({
    type: user,
    condition: ({ newRecord }) => newRecord.email.endsWith("@tailor.tech"),
  }),
  operation: {
    kind: "function",
    body: async (args) => {
      await userRecordLog(args);
    },
  },
});
The newRecord parameter in the condition function is automatically typed based on the user type definition.

Workflow Type Safety

Workflow jobs have fully typed inputs and outputs:
workflows/sample.ts
import { createWorkflow, createWorkflowJob } from "@tailor-platform/sdk";
import { format } from "date-fns";
import { getDB } from "../generated/tailordb";

export const process_payment = createWorkflowJob({
  name: "process-payment",
  body: async () => {
    const db = getDB("tailordb");
    const invoices = await db.selectFrom("Invoice").selectAll().execute();
    return invoices.reduce(
      (sum, invoice) => [...sum, invoice.status],
      [] as (typeof invoices)[number]["status"][],
    );
  },
});

export const check_inventory = createWorkflowJob({
  name: "check-inventory",
  body: () => format(new Date(), "yyyy-MM-dd HH:mm:ss"),
});

export const validate_order = createWorkflowJob({
  name: "validate-order",
  body: async (input: { orderId: string }) => {
    console.log("Order ID:", input.orderId);
    const inventoryResult = await check_inventory.trigger();
    const paymentResult = await process_payment.trigger();
    return { inventoryResult, paymentResult };
  },
});

export default createWorkflow({
  name: "sample-workflow",
  mainJob: validate_order,
});

Job Trigger Type Inference

When triggering jobs, the return type is automatically inferred:
workflows/order-processing.ts
import { createWorkflow, createWorkflowJob } from "@tailor-platform/sdk";
import { fetchCustomer } from "./jobs/fetch-customer";
import { sendNotification } from "./jobs/send-notification";

export const processOrder = createWorkflowJob({
  name: "process-order",
  body: async (input: { orderId: string; customerId: string }, { env, user }) => {
    // Fetch customer information using trigger
    const customer = await fetchCustomer.trigger({
      customerId: input.customerId,
    });

    if (!customer) {
      throw new Error(`Customer ${input.customerId} not found`);
    }

    // Send notification to customer using trigger
    const notification = await sendNotification.trigger({
      message: `Your order ${input.orderId} is being processed`,
      recipient: customer.email,
    });

    return {
      orderId: input.orderId,
      customerId: input.customerId,
      customerEmail: customer.email,
      notificationSent: notification.sent,
      processedAt: notification.timestamp,
    };
  },
});

export default createWorkflow({
  name: "order-processing",
  mainJob: processOrder,
});
The .trigger() method returns a Promise with the job’s return type automatically inferred. Always use await to get the result.

Generated Types

The SDK can generate TypeScript types from your TailorDB schema for use with query builders:
tailor.config.ts
import { defineConfig, definePlugins } from "@tailor-platform/sdk";
import { kyselyTypePlugin } from "@tailor-platform/sdk/plugin/kysely-type";

export default defineConfig({
  db: {
    tailordb: {
      files: ["./tailordb/*.ts"],
    },
  },
});

export const plugins = definePlugins(
  kyselyTypePlugin({ distPath: "./generated/tailordb.ts" }),
);
Generate types with:
tailor-sdk generate
Use generated types with getDB():
import { getDB } from "../generated/tailordb";

const db = getDB("tailordb");
// db is fully typed with Kysely query builder

Validation

Validations are type-safe and enforce constraints at runtime:
tailordb/customer.ts
import { db } from "@tailor-platform/sdk";

export const customer = db
  .type("Customer", "Customer information", {
    name: db.string(),
    email: db.string(),
    city: db.string({ optional: true }).validate(
      ({ value }) => (value ? value.length > 1 : true),
      ({ value }) => (value ? value.length < 100 : true),
    ),
    fullAddress: db.string(),
    ...db.fields.timestamps(),
  })
  .validate({
    name: [({ value }) => value.length > 5, "Name must be longer than 5 characters"],
  });

Best Practices

Export Types

Always export both the runtime value and TypeScript type for TailorDB definitions:
export const user = db.type(...);
export type user = typeof user;

Use t.infer

Use t.infer<typeof type> to extract TypeScript types from database schemas instead of manually typing.

Generate Types

Run tailor-sdk generate to create Kysely types for type-safe database queries.

Describe Everything

Add .description() to inputs and outputs for better GraphQL documentation.

Next Steps

Configuration

Learn about tailor.config.ts structure

Deployment

Understand the deployment workflow