CMS Relationships
Brightsy AI CMS supports defining relationships between record types using has-one and has-many relationship fields. These allow you to create structured data models with foreign key relationships.
Relationship Types
Has-One Relationships
A has-one relationship allows a record to reference a single record from another record type.
Example: Blog Post with Author
{
"type": "object",
"required": ["title", "content"],
"properties": {
"title": {
"type": "string",
"title": "Title"
},
"content": {
"type": "string",
"format": "textarea",
"title": "Content"
},
"author": {
"type": "object",
"format": "has-one",
"title": "Author",
"properties": {
"relationship": {
"type": "string",
"default": "has-one"
},
"contentType": {
"type": "string",
"default": "authors"
},
"id": {
"type": "string",
"title": "Author ID"
}
},
"required": ["id"]
}
}
}
The has-one relationship will:
- Display a dropdown selector with available records from the related record type
- Show a "View related record" link when a record is selected
- In data tables, automatically fetch and display the related record's label
- Store the relationship as:
{ relationship: "has-one", contentType: "authors", id: "record-id" }
Has-Many Relationships
A has-many relationship displays all records from another record type that reference the current record via a foreign key field.
Example: Author with Blog Posts
First, create an "authors" record type. Then create a "blog-posts" record type with a foreign key:
{
"type": "object",
"required": ["title", "author_id"],
"properties": {
"title": {
"type": "string",
"title": "Title"
},
"author_id": {
"type": "string",
"title": "Author ID"
},
"content": {
"type": "string",
"format": "textarea",
"title": "Content"
}
}
}
Then, add a has-many field to the "authors" record type:
{
"type": "object",
"required": ["name"],
"properties": {
"name": {
"type": "string",
"title": "Name"
},
"email": {
"type": "string",
"format": "email",
"title": "Email"
},
"posts": {
"type": "object",
"format": "has-many",
"title": "Blog Posts",
"properties": {
"relationship": {
"type": "string",
"default": "has-many"
},
"contentType": {
"type": "string",
"default": "blog-posts"
},
"foreignKey": {
"type": "string",
"default": "author_id"
}
}
}
}
}
The has-many relationship will:
- Display a list of all related records in the edit form
- Show links to view/edit each related record
- Provide a link to create new related records with the foreign key pre-filled
- Automatically query records where
foreignKeyfield matches the current record's ID - In data tables, show a simple indicator (e.g., "blog-posts (has-many)")
Schema Properties
Has-One Schema
{
"type": "object",
"format": "has-one",
"title": "Field Label",
"properties": {
"relationship": {
"type": "string",
"default": "has-one"
},
"contentType": {
"type": "string",
"default": "target-record-type-slug"
},
"id": {
"type": "string",
"title": "Reference ID"
}
},
"required": ["id"]
}
- relationship: Always set to
"has-one" - contentType: The slug of the record type to reference
- id: Stores the ID of the selected record
Has-Many Schema
{
"type": "object",
"format": "has-many",
"title": "Field Label",
"properties": {
"relationship": {
"type": "string",
"default": "has-many"
},
"contentType": {
"type": "string",
"default": "related-record-type-slug"
},
"foreignKey": {
"type": "string",
"default": "field-name-in-related-records"
}
}
}
- relationship: Always set to
"has-many" - contentType: The slug of the related record type
- foreignKey: The field name in the related record type that stores the reference to the current record
Complete Example: Blog System
Authors Record Type
{
"type": "object",
"required": ["name", "email"],
"properties": {
"name": {
"type": "string",
"title": "Name"
},
"email": {
"type": "string",
"format": "email",
"title": "Email"
},
"bio": {
"type": "string",
"format": "textarea",
"title": "Biography"
},
"posts": {
"type": "object",
"format": "has-many",
"title": "Blog Posts",
"description": "All blog posts written by this author",
"properties": {
"relationship": {
"type": "string",
"default": "has-many"
},
"contentType": {
"type": "string",
"default": "blog-posts"
},
"foreignKey": {
"type": "string",
"default": "author_id"
}
}
}
}
}
Blog Posts Record Type
{
"type": "object",
"required": ["title", "content", "author"],
"properties": {
"title": {
"type": "string",
"title": "Title"
},
"slug": {
"type": "string",
"title": "URL Slug"
},
"content": {
"type": "string",
"format": "textarea",
"title": "Content"
},
"author": {
"type": "object",
"format": "has-one",
"title": "Author",
"properties": {
"relationship": {
"type": "string",
"default": "has-one"
},
"contentType": {
"type": "string",
"default": "authors"
},
"id": {
"type": "string",
"title": "Author ID"
}
},
"required": ["id"]
},
"author_id": {
"type": "string",
"title": "Author ID (Foreign Key)"
},
"published_date": {
"type": "string",
"format": "date",
"title": "Published Date"
}
}
}
Using the API
Querying Records with Relationships
When you fetch a record via the CMA or CDA API, relationship fields are included in the response:
// Has-one relationship value
{
"author": {
"relationship": "has-one",
"contentType": "authors",
"id": "abc123"
}
}
// Has-many relationship value
{
"posts": {
"relationship": "has-many",
"contentType": "blog-posts",
"foreignKey": "author_id"
}
}
Populating Relationships (Coming Soon)
The populate feature will allow you to automatically include related record data:
const client = createClient({
apiUrl: 'https://your-instance.brightsy.ai',
apiKey: 'your-api-key'
});
// This will include the full author record in the response
const posts = await client
.cma('account-slug')
.recordType('blog-posts')
.populate('author')
.get();
Best Practices
Consistent Naming: Use consistent field names for foreign keys (e.g., always use
author_id, not sometimesauthorId)Required Fields: Mark relationship fields as required if they're essential to your data model
Circular References: Avoid circular relationships (A → B → A) as they can cause performance issues
Foreign Key Fields: Always include both the relationship field (for the UI) and a simple string field for the foreign key (for querying)
Record Type Slugs: Use descriptive, URL-friendly slugs for record types (e.g.,
blog-posts, notblogPostsorBlogPosts)
Limitations
- Relationships are currently single-level (no nested population)
- Many-to-many relationships require a junction table approach
- Related records must be in the same account
Future Enhancements
- Automatic population via API
- Bi-directional relationship management
- Many-to-many relationship support
- Relationship validation and constraints
- Cascading deletes configuration