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.

TailorDB is a type-safe database service that provides automatic GraphQL API generation, relations with foreign key constraints, and a powerful permission system.

Overview

TailorDB provides:
  • Type-safe schema definitions using TypeScript
  • Automatic GraphQL API generation (CRUD operations)
  • Relations between types with automatic index and foreign key constraints
  • Permission system for access control
  • Field-level hooks and validations
For the official Tailor Platform documentation, see TailorDB Guide.

Type Definition

Define TailorDB Types in files matching glob patterns specified in tailor.config.ts.
Definition Rules:
  • Multiple types per file: You can define multiple TailorDB types in a single file
  • Export method: Use named exports (export const)
  • Export both value and type: Always export both the runtime value and TypeScript type
  • Uniqueness: Type names must be unique across all TailorDB files
import { db } from "@tailor-platform/sdk";

// Export both value and type
export const user = db.type("User", {
  name: db.string(),
  email: db.string().unique(),
  age: db.int(),
  ...db.fields.timestamps(),
});
export type user = typeof user;

// You can define multiple types in the same file
export const role = db.type("Role", {
  name: db.string().unique(),
});
export type role = typeof role;

Type Name Customization

Specify plural form by passing an array as first argument:
db.type(["User", "UserList"], {
  name: db.string(),
});
Pass a description as second argument:
db.type("User", "User in the system", {
  name: db.string(),
});

Field Types

TailorDB supports various field types with TypeScript type safety:
db.string()
String
String field type
db.int()
Integer
Integer field type
db.float()
Float
Floating point number field type
db.bool()
Boolean
Boolean field type
db.date()
Date
Date field type (string in TypeScript)
db.datetime()
DateTime
DateTime field type (string | Date in TypeScript)
db.time()
Time
Time field type (string in TypeScript)
db.uuid()
UUID
UUID field type (string in TypeScript)
db.enum()
Enum
Enum field type with predefined values
db.object()
Nested Object
Nested object field type

Optional and Array Fields

db.string({ optional: true });
db.string({ array: true });
db.string({ optional: true, array: true });

Enum Fields

db.enum(["red", "green", "blue"]);
db.enum([
  { value: "active", description: "Active status" },
  { value: "inactive", description: "Inactive status" },
]);

Object Fields

Define nested object structures:
example/tailordb/nested.ts
import { db } from "@tailor-platform/sdk";

export const nestedProfile = db.type("NestedProfile", "Nested Profile Type", {
  userInfo: db
    .object({
      name: db.string().description("User's full name"),
      age: db.int({ optional: true }).description("User's age"),
      bio: db.string({ optional: true }).description("User's biography"),
      email: db.string().description("User's email address"),
      phone: db.string({ optional: true }).description("User's phone number"),
    })
    .description("User information"),
  metadata: db
    .object({
      created: db.datetime().description("Creation timestamp"),
      lastUpdated: db.datetime({ optional: true }).description("Last update timestamp"),
      version: db.int().description("Version number"),
    })
    .description("Profile metadata"),
  archived: db.bool({ optional: true }).description("Archive status"),
  ...db.fields.timestamps(),
});

Field Modifiers

Description

db.string().description("User's full name");

Index / Unique

db.string().index();
db.string().unique();

Relations

Add a relation to field with automatic index and foreign key constraint:
example/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 }),
  cancelReason: db.string({ optional: true }),
  canceledAt: db.datetime({ optional: true }),
  ...db.fields.timestamps(),
});
Relation Types:
  • "n-1" - Many-to-one relation (creates relation field in both directions)
  • "1-1" - One-to-one relation
  • "keyOnly" - Foreign key constraint without creating a relation field
Create relations against different fields using toward.key:
const user = db.type("User", {
  email: db.string().unique(),
});

const userProfile = db.type("UserProfile", {
  userEmail: db.string().relation({
    type: "1-1",
    toward: { type: user, key: "email" },
  }),
});
Customize relation names using toward.as / backward options:
const userProfile = db.type("UserProfile", {
  userId: db.uuid().relation({
    type: "1-1",
    toward: { type: user, as: "base" },
    backward: "profile",
  }),
});
This generates the following GraphQL types:
type UserProfile {
  userId: ID!
  base: User # toward.as: access User from UserProfile
}

type User {
  id: ID!
  profile: UserProfile # backward: access UserProfile from User
}

Hooks

Add hooks to execute functions during data creation or update. Hooks receive three arguments:
  • value: User input if provided, otherwise existing value on update or null on create
  • data: Entire record data (for accessing other field values)
  • user: User performing the operation

Field-level Hooks

Set hooks directly on individual fields:
db.string().hooks({
  create: ({ user }) => user.id,
  update: ({ value }) => value,
});
When setting hooks at the field level, the data argument type is unknown since the field doesn’t know about other fields in the type. Use type-level hooks if you need to access other fields with type safety.

Type-level Hooks

Set hooks for multiple fields at once using db.type().hooks():
example/tailordb/customer.ts
import { db } from "@tailor-platform/sdk";

export const customer = db
  .type("Customer", "Customer information", {
    name: db.string(),
    email: db.string(),
    phone: db.string({ optional: true }),
    country: db.string(),
    postalCode: db.string(),
    address: db.string({ optional: true }),
    city: db.string({ optional: true }),
    fullAddress: db.string(),
    state: db.string(),
    ...db.fields.timestamps(),
  })
  .hooks({
    fullAddress: {
      create: ({ data }) => `${data.postalCode} ${data.address} ${data.city}`,
      update: ({ data }) => `${data.postalCode} ${data.address} ${data.city}`,
    },
  });
Important: Field-level and type-level hooks cannot coexist on the same field. TypeScript will prevent this at compile time.

Validation

Add validation rules to fields. Validators receive three arguments (executed after hooks):
  • value: Field value after hook transformation
  • data: Entire record data after hook transformations
  • user: User performing the operation
Validators return true for success, false for failure. Use array form [validator, errorMessage] for custom error messages.

Field-level Validation

db.string().validate(
  ({ value }) => value.includes("@"),
  [({ value }) => value.length >= 5, "Email must be at least 5 characters"],
);

Type-level Validation

example/tailordb/customer.ts
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),
    ),
  })
  .validate({
    name: [({ value }) => value.length > 5, "Name must be longer than 5 characters"],
  });
Important: Field-level and type-level validation cannot coexist on the same field. TypeScript will prevent this at compile time.
db.string().vector();

Serial / Auto-increment

db.int().serial({
  start: 0,
  maxValue: 100,
});

db.string().serial({
  start: 0,
  format: "CUST_%d",
});

Common Fields

export const user = db.type("User", {
  name: db.string(),
  ...db.fields.timestamps(),
});

Type Modifiers

Composite Indexes

example/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(),
  })
  .indexes(
    { fields: ["name", "department"], unique: false },
    { fields: ["status", "createdAt"], unique: false, name: "user_status_created_idx" },
  );

File Fields

example/tailordb/salesOrder.ts
db.type("SalesOrder", {
  totalPrice: db.int(),
  status: db.string(),
})
.files({
  receipt: "receipt file",
  form: "order form file",
});

Features

db.type("User", {
  name: db.string(),
})
.features({
  aggregation: true,
  bulkUpsert: true,
});

Permissions

Configure Permission and GQLPermission. For details, see the TailorDB Permission documentation.
Important: Following the secure-by-default principle, all operations are denied if permissions are not configured. You must explicitly grant permissions for each operation (create, read, update, delete).
example/tailordb/permissions.ts
import type {
  PermissionCondition,
  TailorTypePermission,
  TailorTypeGqlPermission,
} from "@tailor-platform/sdk";

const defaultMachineUser = [
  { user: "role" },
  "=",
  "MANAGER",
] as const satisfies PermissionCondition;
const loggedIn = [{ user: "_loggedIn" }, "=", true] as const satisfies PermissionCondition;

export const defaultPermission: TailorTypePermission = {
  create: [defaultMachineUser],
  read: [defaultMachineUser, loggedIn],
  update: [defaultMachineUser],
  delete: [defaultMachineUser],
};

export const defaultGqlPermission: TailorTypeGqlPermission = [
  {
    conditions: [defaultMachineUser],
    actions: ["create", "read", "update", "delete", "aggregate", "bulkUpsert"],
    permit: true,
  },
  {
    conditions: [loggedIn],
    actions: ["read"],
    permit: true,
  },
];

Development/Test Helpers

For local development, prototyping, or testing, the SDK provides helper constants that grant full access without conditions:
import {
  db,
  unsafeAllowAllTypePermission,
  unsafeAllowAllGqlPermission,
} from "@tailor-platform/sdk";

db.type("User", {
  name: db.string(),
})
  .permission(unsafeAllowAllTypePermission)
  .gqlPermission(unsafeAllowAllGqlPermission);
Do not use unsafeAllowAllTypePermission or unsafeAllowAllGqlPermission in production environments as they effectively disable authorization checks.

Complete Example

example/tailordb/user.ts
import { db } from "@tailor-platform/sdk";
import { defaultGqlPermission, defaultPermission } from "./permissions";

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" },
  )
  .permission(defaultPermission)
  .gqlPermission(defaultGqlPermission);

export type user = typeof user;

Build docs developers (and LLMs) love