Relations connect data between collections, like linking products to categories or posts to authors. Crouton stores relations as foreign key IDs only - it does not embed full related objects. Most apps can start simple by storing these IDs and querying manually when needed:
// Store the relationship
const product = {
id: '123',
name: 'Widget',
categoryId: 'cat-456' // ← Just store the ID
}
// Query when needed
const category = await db.select()
.from(categories)
.where(eq(categories.id, product.categoryId))
This approach works well when you:
It's the recommended starting point for most applications.
refTargetThe generator automatically creates UI components for reference fields when you use the refTarget property in your schema:
{
"authorId": {
"type": "string",
"refTarget": "authors",
"meta": {
"required": true,
"label": "Author"
}
}
}
By default, references are scoped to the current layer. To reference collections outside your layer (like a shared users table), prefix with a colon:
{
"updatedBy": {
"type": "string",
"refTarget": ":users",
"meta": {
"label": "Updated By"
}
}
}
This generates collection="users" instead of collection="shopUsers", allowing references to global or external collections.
The generator creates a CroutonFormReferenceSelect component that provides:
<!-- Generated automatically in _Form.vue -->
<UFormField label="Author" name="authorId">
<CroutonFormReferenceSelect
v-model="state.authorId"
collection="authors"
label="Author"
/>
</UFormField>
In table views, reference fields display as CroutonItemCardMini components showing the referenced item's title with a quick-edit button:
<!-- Generated automatically in List.vue -->
<template #authorId-cell="{ row }">
<CroutonItemCardMini
v-if="row.original.authorId"
:id="row.original.authorId"
collection="authors"
/>
</template>
This provides a seamless experience for managing related data without manual coding.
For apps with lots of related data queries, Drizzle relations let you fetch related data in one query, avoiding N+1 query problems:
// Define relation (one-time setup in schema.ts)
import { relations } from 'drizzle-orm'
export const productsRelations = relations(products, ({ one }) => ({
category: one(categories, {
fields: [products.categoryId],
references: [categories.id]
})
}))
// Query with automatic join
const product = await db.query.products.findFirst({
where: eq(products.id, '123'),
with: { category: true } // ← Drizzle handles the join
})
// Now product.category is populated in ONE query
console.log(product.category.name)
When to use this:
✅ DO:
useCollectionQuery to fetch related collections for dropdowns❌ DON'T:
category?.name || 'N/A')Patterns Overview
This section explores common patterns and best practices for working with data in Nuxt Crouton. Learn how to build forms, tables, relationships, and integrate with Drizzle ORM.
Form Patterns
Working with forms in Nuxt Crouton - custom fields, validation, relation dropdowns, and architecture