{
  "slug": "relationships",
  "title": "Content Relationships",
  "description": "Connect documents across collections with relation fields — single, multi, and reverse lookups.",
  "category": "guides",
  "order": 6,
  "locale": "en",
  "translationGroup": "88a07cc8-f0b8-4298-be7b-4c97f93779b0",
  "helpCardId": null,
  "content": "## How relations work\n\nRelations connect documents across collections. A relation field stores a **slug string** (single) or **slug array** (multiple) — never embedded data.\n\n```typescript\n// Single relation — stores one slug, e.g. \"john-doe\"\n{ name: 'author', type: 'relation', collection: 'team' }\n\n// Multi relation — stores slug array, e.g. [\"guide-1\", \"guide-2\"]\n{ name: 'relatedPosts', type: 'relation', collection: 'posts', multiple: true }\n```\n\n## Resolving relations\n\nSince relations store slugs, resolve them with `getDocument()`:\n\n```typescript\nfunction resolveRelation(collection: string, slug: string | null) {\n  if (!slug) return null;\n  return getDocument(collection, slug);\n}\n\nfunction resolveRelations(collection: string, slugs: string[] | null) {\n  if (!slugs?.length) return [];\n  return slugs\n    .map(slug => getDocument(collection, slug))\n    .filter(Boolean);\n}\n```\n\n## Pattern: Blog post with author\n\n```typescript\nexport default async function PostPage({ params }) {\n  const { slug } = await params;\n  const post = getDocument('posts', slug);\n  if (!post) notFound();\n\n  // Resolve author\n  const author = post.data.author\n    ? getDocument('team', post.data.author)\n    : null;\n\n  // Resolve related posts\n  const related = (post.data.relatedPosts ?? [])\n    .map(s => getDocument('posts', s))\n    .filter(Boolean);\n\n  return (\n    <article>\n      <h1>{post.data.title}</h1>\n      {author && (\n        <div>\n          <img src={author.data.photo} alt={author.data.name} />\n          <p>{author.data.name}</p>\n        </div>\n      )}\n    </article>\n  );\n}\n```\n\n## Reverse lookup\n\nFind all documents that reference a given slug:\n\n```typescript\n// All posts by a specific author\nconst posts = getCollection('posts')\n  .filter(post => post.data.author === authorSlug);\n```\n\n## When to use relations vs. embedded data\n\n**Use relations** when:\n- Data is shared across multiple documents (e.g., author on many posts)\n- Related data changes independently\n- You need a canonical source of truth\n\n**Use embedded data** (object/array fields) when:\n- Data is unique to this document\n- Data doesn't need independent querying\n- Simpler structure without cross-collection lookups",
  "excerpt": "How relations work\n\nRelations connect documents across collections. A relation field stores a slug string (single) or slug array (multiple) — never embedded data.\n\ntypescript\n// Single relation — stores one slug, e.g. \"john-doe\"\n{ name: 'author', type: 'relation', collection: 'team' }\n\n// Multi rela",
  "seo": {
    "metaTitle": "Content Relationships — webhouse.app Docs",
    "metaDescription": "Connect documents across collections with relation fields — single, multi, and reverse lookups.",
    "keywords": [
      "webhouse",
      "cms",
      "guides"
    ]
  },
  "createdAt": "2026-03-29T21:56:59.902Z",
  "updatedAt": "2026-03-29T21:56:59.902Z"
}