Next.js

Multitenancy

Implementing multi-tenant architecture using Next.js Middleware and dynamic routing.

Routing Middleware: Next.js Middleware intercepts requests and rewrites them based on the hostname or subdomain to a dynamic tenant route.
📄middleware.ts
TS
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'

export function middleware(request: NextRequest) {
  const url = request.nextUrl
  
  // Get hostname of request (e.g. demo.vercel.pub, demo.localhost:3000)
  let hostname = request.headers.get('host')!
  
  // Allow local routing
  hostname = hostname.replace('.localhost:3000', '')

  const searchParams = request.nextUrl.searchParams.toString()
  const path = `${url.pathname}${searchParams.length > 0 ? `?${searchParams}` : ''}`

  // Rewrite for subdomains or custom domains
  if (hostname !== 'localhost:3000' && hostname !== 'vercel.pub') {
    return NextResponse.rewrite(new URL(`/${hostname}${path}`, request.url))
  }

  return NextResponse.next()
}

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    '/((?!api|_next/static|_next/image|favicon.ico).*)',
  ],
}
Tenant Dynamic Route: A dynamic route catch-all that receives the tenant identifier (e.g., domain) as a parameter and fetches tenant-specific data.
📄app/[tenant]/page.tsx
TSX
import { notFound } from 'next/navigation'

async function getTenantData(domain: string) {
  // Fetch tenant data from database based on domain
  const res = await fetch(`https://api.example.com/tenants/${domain}`)
  
  if (!res.ok) return null
  return res.json()
}

export default async function TenantPage({ params }: { params: { tenant: string } }) {
  const tenant = await getTenantData(params.tenant)
  
  if (!tenant) {
    notFound()
  }

  return (
    <div>
      <h1>Welcome to {tenant.name}</h1>
      <p>This is a multi-tenant page mapped to the domain: {params.tenant}</p>
    </div>
  )
}