Multi-tenancy and Edge Routing
Relevant source files
The following files were used as context for generating this wiki page:
Nordcom Commerce is a multi-tenant platform designed to serve multiple independent shops from a single Next.js deployment. This architecture relies on edge-based hostname resolution to identify tenants and route requests to the appropriate context without requiring redeployments for new shops.
Sources: README.md:12-14, CONTEXT.md:7-9
Core Architecture and Tenant Resolution
The system resolves tenants by analyzing the inbound Host header (or x-forwarded-host) in the Next.js middleware. This process translates a raw hostname into a canonical Shop entity retrieved from the Convex data layer.
Resolution Logic
- Normalization: The middleware strips ports,
.localhostsuffixes, and Vercel preview suffixes from the hostname. - Database Lookup: The normalized hostname is passed to
Shop.findByDomain(hostname), which queries theshopstable in Convex. - Rewriting: Upon a successful match, the middleware rewrites the internal URL to include the resolved domain and locale, specifically under the
/[domain]/[locale]/...segment. - Fallback: If no shop is found, the request is rewritten to a status page on the Service Domain (e.g.,
/status/unknown-shop/).
Sources: README.md:144-152, apps/storefront/README.md:43-51, CONTEXT.md:16-18
Routing Flow Diagram
The following diagram illustrates how a request is intercepted at the edge and routed to a specific tenant.
Domain Management
Tenancy supports multiple domain types to provide flexibility for development and production environments.
| Term | Definition |
|---|---|
| Shop Domain | The canonical primary hostname for a shop, unique across the platform. |
| Alternative Domain | Additional hostnames that resolve to the same shop. |
| Service Domain | A global fallback host for non-tenant-scoped status pages. |
| Hostname | The raw, pre-resolution host header from the request. |
Sources: CONTEXT.md:16-36
Domain Verification
The platform includes an informational check to ensure a domain points to the storefront's Vercel project or the Service Domain. This check updates a status from pending to verified or failed. Crucially, this verification does not gate routing; hostname resolution proceeds regardless of verification status to ensure uptime.
Sources: CONTEXT.md:38-46
Locale Fallback and Pathing
Routing is not only tenant-aware but also locale-aware. Every tenant-scoped request includes an xx-XX language/region tag.
Locale Resolution Chain
When a request arrives without a locale segment, the middleware consults the Locale Fallback Chain:
- Request Locale: Any locale already present in the URL path.
- Shop Default: The
defaultLocalestored on the Shop record in the database. - Platform Safety: A code-level fallback (usually
en-US) if the shop record is malformed.
Sources: CONTEXT.md:57-69, CONTEXT.md:405-410
Sources: CONTEXT.md:66-69, README.md:144-152
Implementation Details
Multi-tenant Middleware
The entry point for routing logic is located in the storefront's proxy file. It differentiates between administrative traffic and customer-facing storefront traffic.
// apps/storefront/src/proxy.ts
// Dispatch logic (Conceptual)
if (req.nextUrl.pathname.startsWith('/admin')) {
return adminMiddleware(req);
}
return storefrontMiddleware(req);
Sources: apps/storefront/src/proxy.ts:1-10, apps/storefront/README.md:43-45
Tenant Context
The system enforces an explicit tenant context. Data fetching helpers and API clients require { shop, locale } to be passed manually. There is no implicit "current shop" global variable to prevent cache-poisoning and cross-tenant data leaks.
Sources: CLAUDE.md:111-114, CONTEXT.md:393-396
Cache Isolation
Multi-tenancy is extended to the caching layer via Tenant-scoped Caching. Every cached entity includes the Shop's unique ID in its cache tags.
| Cache Level | Tag Format |
|---|---|
| Tenant Level | shopify.<shopId> |
| Entity Level | shopify.<shopId>.product.<handle> |
| CMS Level | cms.<shopId>.<collection>.<key> |
Sources: apps/storefront/README.md:95-101, packages/cms/README.md:126-130
Security and Identity
- Provider Tokens: Sensitive credentials (like Shopify API tokens) are stored on the Shop record but are projected out by default. They are protected with React's
experimental_taintUniqueValueto prevent them from being passed to Client Components. - Collaborators: Access to the Admin dashboard for a specific shop is governed by a list of Collaborators with specific permissions, which is mirrored in the CMS to ensure consistent access predicates.
Sources: apps/storefront/README.md:139-141, CONTEXT.md:255-257
Conclusion
Multi-tenancy and Edge Routing in Nordcom Commerce enable a "Software as a Service" model where adding a new tenant is as simple as inserting a row into the Convex shops table. By performing resolution at the middleware level and enforcing explicit context in all downstream calls, the platform maintains strict isolation between tenants while sharing a single highly-optimized infrastructure.