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.
Relations in TailorDB connect types together through foreign key constraints. Relations automatically create database indexes and generate GraphQL fields for traversing relationships.
Basic Relation Syntax
Add a relation using the .relation() modifier on a field:
fieldName.relation({
type: "n-1" | "1-1" | "keyOnly",
toward: {
type: targetType,
as?: string,
key?: string,
},
backward?: string,
})
type
'n-1' | '1-1' | 'keyOnly'
required
The relation type:
"n-1" or "manyToOne": Many-to-one (creates a relation field)
"1-1" or "oneToOne": One-to-one (creates a relation field and unique constraint)
"keyOnly": Foreign key constraint only (no GraphQL relation field)
The target type to relate to.
Custom name for the forward relation field in GraphQL.
Target field to reference (defaults to "id").
Custom name for the backward relation field in GraphQL.
Many-to-One Relations (n-1)
The most common relation type. Many records can reference one parent record.
import { db } from "@tailor-platform/sdk";
import { customer } from "./customer";
export const salesOrder = db.type("SalesOrder", {
customerID: db.uuid().relation({
type: "n-1",
toward: { type: customer },
}),
totalPrice: db.int({ optional: true }),
discount: db.float({ optional: true }),
...db.fields.timestamps(),
});
This creates:
- A foreign key constraint from
salesOrder.customerID to customer.id
- An index on
salesOrder.customerID
- A GraphQL field
customer on SalesOrder for accessing the related customer
- A GraphQL field
salesOrders on Customer for accessing all related orders
GraphQL schema:
type SalesOrder {
id: ID!
customerID: ID!
customer: Customer # Forward relation
totalPrice: Int
discount: Float
}
type Customer {
id: ID!
salesOrders: [SalesOrder!]! # Backward relation
}
One-to-One Relations (1-1)
One record relates to exactly one other record. Automatically adds a unique constraint.
import { db } from "@tailor-platform/sdk";
import { user } from "./user";
export const userSetting = db.type("UserSetting", {
language: db.enum(["jp", "en"]),
userID: db.uuid().relation({
type: "1-1",
toward: { type: user },
backward: "setting",
}),
...db.fields.timestamps(),
});
This creates:
- A foreign key constraint from
userSetting.userID to user.id
- An index on
userSetting.userID
- A unique constraint on
userSetting.userID (ensuring one-to-one)
- A GraphQL field
user on UserSetting
- A GraphQL field
setting on User (custom name via backward)
GraphQL schema:
type UserSetting {
id: ID!
language: String!
userID: ID!
user: User # Forward relation
}
type User {
id: ID!
setting: UserSetting # Backward relation (note singular)
}
Key-Only Relations
Creates a foreign key constraint without generating GraphQL relation fields. Useful for references that don’t need traversal.
export const salesOrder = db.type("SalesOrder", {
approvedByUserIDs: db.uuid({ optional: true, array: true }).relation({
type: "keyOnly",
toward: { type: user },
}),
totalPrice: db.int({ optional: true }),
});
This creates:
- A foreign key constraint ensuring
approvedByUserIDs values exist in user.id
- No GraphQL relation fields (only the ID array is exposed)
Customizing Relation Names
Use toward.as and backward to customize GraphQL field names:
import { db } from "@tailor-platform/sdk";
import { salesOrder } from "./salesOrder";
export const invoice = db.type("Invoice", {
invoiceNumber: db.string(),
salesOrderID: db.uuid().relation({
type: "1-1",
toward: { type: salesOrder, as: "order" },
backward: "invoice",
}),
amount: db.int({ optional: true }),
});
GraphQL schema:
type Invoice {
id: ID!
salesOrderID: ID!
order: SalesOrder # Custom name via "as"
amount: Int
}
type SalesOrder {
id: ID!
invoice: Invoice # Custom name via "backward"
}
Relations to Non-ID Fields
By default, relations target the id field. Use toward.key to reference a different field:
export const user = db.type("User", {
email: db.string().unique(),
name: db.string(),
});
export const userProfile = db.type("UserProfile", {
userEmail: db.string().relation({
type: "1-1",
toward: { type: user, key: "email" },
}),
bio: db.string(),
});
Requirements:
- The target field (
email in this example) must have a unique constraint
- The relation field type must match the target field type
Self-Referencing Relations
Types can reference themselves using "self":
export const category = db.type("Category", {
name: db.string(),
parentID: db.uuid({ optional: true }).relation({
type: "n-1",
toward: { type: "self", as: "parent" },
backward: "children",
}),
});
GraphQL schema:
type Category {
id: ID!
name: String!
parentID: ID
parent: Category # Self-reference
children: [Category!]! # Backward relation
}
Automatic Index Creation
All relations automatically create database indexes on the source field:
export const salesOrder = db.type("SalesOrder", {
customerID: db.uuid().relation({
type: "n-1",
toward: { type: customer },
}),
// Index automatically created on customerID
});
You can add additional composite indexes if needed:
export const salesOrder = db.type("SalesOrder", {
customerID: db.uuid().relation({
type: "n-1",
toward: { type: customer },
}),
status: db.string({ optional: true }),
})
.indexes(
{ fields: ["customerID", "status"], unique: false },
);
Complete Example
Here’s a complete example showing multiple relation types:
import { db } from "@tailor-platform/sdk";
// Parent type
export const supplier = db.type("Supplier", {
name: db.string(),
email: db.string({ optional: true }),
...db.fields.timestamps(),
});
export type supplier = typeof supplier;
// Child type with relation
export const purchaseOrder = db.type("PurchaseOrder", {
supplierID: db.uuid().relation({
type: "n-1",
toward: { type: supplier },
}),
totalPrice: db.int(),
discount: db.float({ optional: true }),
status: db.string(),
...db.fields.timestamps(),
});
export type purchaseOrder = typeof purchaseOrder;
Generated GraphQL:
type PurchaseOrder {
id: ID!
supplierID: ID!
supplier: Supplier # Automatically generated
totalPrice: Int!
discount: Float
status: String!
createdAt: DateTime!
updatedAt: DateTime
}
type Supplier {
id: ID!
name: String!
email: String
purchaseOrders: [PurchaseOrder!]! # Automatically generated
createdAt: DateTime!
updatedAt: DateTime
}
Relation Type Summary
| Relation Type | Foreign Key | Index | Unique | GraphQL Relations |
|---|
"n-1" (many-to-one) | ✓ | ✓ | ✗ | ✓ (forward & backward) |
"1-1" (one-to-one) | ✓ | ✓ | ✓ | ✓ (forward & backward) |
"keyOnly" | ✓ | ✗ | ✗ | ✗ |
Best Practices
- Use UUID fields for foreign keys: Most relations use
db.uuid() to reference the auto-generated id field
- Always import parent types: Import the type you’re relating to at the top of the file
- Name fields with ID suffix: Convention is to name relation fields like
customerID, userID, etc.
- Export both value and type: Always export both for TypeScript inference
- Consider composite indexes: Add indexes on commonly queried field combinations
See Also