Skip to content

feat: tenant-slug-based routing with per-request tenant context#6

Draft
Wedvich wants to merge 1 commit into
mainfrom
feat/tenant-slug-routing
Draft

feat: tenant-slug-based routing with per-request tenant context#6
Wedvich wants to merge 1 commit into
mainfrom
feat/tenant-slug-routing

Conversation

@Wedvich
Copy link
Copy Markdown
Owner

@Wedvich Wedvich commented Mar 21, 2026

Summary

  • Adds Linear-style tenant-slug routing (/:tenantSlug/*) so tenant context is explicit in the URL
  • Introduces per-request X-Tenant-Slug header architecture — the session stores identity only, tenant context is resolved per request via new middleware
  • Enables multi-tab support: two browser tabs can view different tenants simultaneously

Backend

  • New tenant-context middleware resolves slug → tenant, validates membership (with 60s cache), sets req.tenantContext
  • Data routes (/api/sync/bootstrap, /api/user/me/events) now read tenant from req.tenantContext instead of session
  • Google callback redirects to /<slug>/ after login/register
  • Expanded reserved slugs list (added callback, error, health, invite, logout)

Frontend

  • Routes restructured: /:tenantSlug/ (home), /:tenantSlug/settings, with /login and /register at root
  • New TenantShell layout route manages slug context, sync bootstrap, and access denied handling
  • New RootRedirect sends authenticated users to their default tenant
  • apiFetch sends X-Tenant-Slug header on every request
  • SyncStore.reset() clears data on tenant switch before re-bootstrapping

Still TODO

  • Manual end-to-end testing with real tenants
  • Test multi-tenant switching across tabs
  • Update docs (plan.md, recap.md, sync-architecture.md)
  • Consider tenant-context middleware unit tests

Test plan

  • yarn turbo typecheck passes
  • yarn turbo lint passes
  • yarn turbo test passes (104 API tests, 15 web tests)
  • Manual: login → verify redirect to /<slug>/
  • Manual: navigate between /<slug>/ and /<slug>/settings
  • Manual: navigate to slug without membership → access denied
  • Manual: two tabs with different tenant slugs → independent data

🤖 Generated with Claude Code

…context

Routes are now /:tenantSlug/* (e.g. /dev-tenant/settings). Public routes
(/login, /register) stay at root level. A new tenant-context middleware
resolves the X-Tenant-Slug header per request, validates membership, and
sets req.tenantContext — the session stores only identity, not tenant
context. This enables multi-tab support with different tenants.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant