Skip to Content
Architecture

Architecture

App segments

The app is split into three main areas:

  • (frontend) — Tenant sites. Each request whose path is not admin, api, or docs is rewritten so the host becomes the first segment: /:tenantDomain/:path*. All tenant pages and data are scoped to that tenant.
  • (payload) — Admin panel and Payload API. Served at /admin and under /api as needed. Not subject to tenant rewrites.
  • /docs — Documentation (Nextra). Excluded from tenant rewrites so the same docs are available regardless of host.

Multi-tenancy (domain → tenant)

Tenancy is host-based. The incoming request host (e.g. mysite.com or localhost:3000) is used to find a tenant document in the tenants collection (by the domain field). That tenant is then used for layout, SEO, and data fetching.

Flow:

  1. Request arrives (e.g. GET https://mysite.com/).
  2. Rewrite rule in next.config.mjs applies only when the path does not match admin, api, or docs. The host is captured and the request is rewritten to /:tenantDomain/:path* (e.g. mysite.com → tenantDomain).
  3. The frontend layout or data layer looks up the tenant by this domain (e.g. via getPayload().find({ collection: 'tenants', where: { domain: { equals: tenantDomain } } })).
  4. The tenant document is used for theme, site name, and any tenant-scoped queries. Pages and posts are filtered by tenant where applicable.

Resolving the tenant

Tenant resolution typically happens in:

  • Layout or root — e.g. [tenantDomain]/layout.tsx receives tenantDomain from the route params (which were set by the rewrite). It may call getPayload() and query the tenants collection with overrideAccess: false if a user is passed, then provide the tenant to the tree (e.g. TenantProvider, or props).
  • SEO — Metadata helpers use the tenant (site name, default title, OG image) when generating titles and Open Graph tags.
  • Data fetching — Pages and posts are often filtered by tenant: equals tenantId so each tenant only sees its own content.

The rewrite configuration lives in next.config.mjs; the tenants collection and access rules are in src/payload.