Multitenancy in Next.js: Subdomain Routing Made Simple

Multitenancy in Next.js: Subdomain Routing Made Simple

Introduction

·

5 min read

Multitenancy is an essential architecture for modern web applications, particularly for SaaS platforms where multiple clients or organizations share a single application instance while maintaining data isolation. One popular approach to managing multitenant applications is through subdomain routing, where each tenant is assigned a unique subdomain (e.g., tenant1.app.com, tenant2.app.com).

In this article, we’ll explore how to implement subdomain routing in a Next.js application using its dynamic routing capabilities in the app directory which was introduced in Next13 and later versions. By the end, you’ll have a clear understanding of how to set up and manage subdomains for your multitenant application.


What is Multitenancy and Subdomain Routing?

What is Multitenancy?

Multitenancy is a software architecture where a single application serves multiple tenants, each with isolated data and configurations. This architecture enables efficient resource sharing while ensuring data security and tenant-specific customizations.

What is Subdomain Routing?

Subdomain routing involves assigning each tenant a unique subdomain to access their portion of the application. For example:

  • Tenant 1: tenant1.app.com

  • Tenant 2: tenant2.app.com

This approach improves tenant isolation, branding, and usability, making it a preferred choice for multitenant apps.


Setting Up Subdomain Routing in Next.js

Prerequisites

  1. A working Next.js project.

  2. DNS configured to handle wildcard subdomains (e.g., *.yourdomain.com).

App Directory Structure

Next.js’s app directory allows you to define dynamic routes easily. To manage tenants via subdomains, you can create a route for tenant-specific pages, but note this would have to be carefully managed by the middleware logic to prevent conflicts within the app, more information about that would be discussed:

app/
 ├── [tenant]/
   ├── page.js
   ├── layout.js

Note: The route name can be any valid file name of your choice

Creating a Dynamic Route

Create a folder named [tenant] in the app directory. Inside this folder, add a page.js file:

export default function TenantPage({ params }) {
  const { tenant } = params;

  return (
    <div>
      <h1>Welcome to {tenant}’s Dashboard!</h1>
    </div>
  );
}

This structure dynamically renders pages based on the tenant’s subdomain.


Handling Dynamic Subdomains

To extract the subdomain from the request, use Next.js’s middleware:

Middleware Setup

Create a middleware.js file in the root of your project, if it’s not already present and add this code:

export function middleware(request) {
  const pathName = request.nextUrl.pathname;
  const url = new URL(request.url);

  const domainObj = request.nextUrl.clone();
  const host = request.headers.get("host") || "";
  const subdomain = host.split(".")[0]; // Extract subdomain

  if (!subdomain || subdomain === "www") return NextResponse.next() // Checks to ensure that the subdomain is not "www" to avoid logic errors

  const isValid = isValidSlug(subdomain);

  if (!isValid && host !== "http://localhost:3000/")  {
    return NextResponse.redirect(
      new URL(`${domainObj.protocol}//localhost:3000/not-found`, request.url)
    );
  } // Handles an invalid subdomain and redirects to a not-found page

 // Note: Use envs to check between environments so localhost:3000 is not checked and redirected to by default

  if (
    isValid &&
    !pathName.startsWith("/api") &&
    !pathName.startsWith("/_next")
   ) {
    return NextResponse.rewrite(new URL(`/${subdomain}`, request.url));
   } // Validates the subdomain and ensures that the pathName is not from the one of the directories in our app

  return NextResponse.next(); // Returns the initial route being accessed if a subdomain is not trying to be accessed
}

export const config = {
  matcher: [
    "/((?!api|_next/static|_next/image|favicon.ico).*)",
  ],
}; // This matches all routes except those that start with api, _next/static, _next/image and match literal route /favicon.ico

function isValidSlug(slug) {
  if (!slug) return false;
  return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(slug);
} // Can be adjusted to any logic you deem fit to validate a subdomain

This middleware ensures that tenant-specific data is passed to your dynamic routes.


Configuring DNS and Hosting for Subdomains

DNS Configuration

To enable wildcard subdomains, set up a DNS record:

  • Type: A or CNAME

  • Name: *

  • Value: Your server’s IP address or domain name.

Hosting Considerations

If deploying to Vercel or any other VPS, ensure your domain’s wildcard subdomain is linked to your project. Add your domain in the Vercel dashboard and select “Enable Wildcard Domain.”


Tenant-Specific Data Handling

To ensure each tenant sees their own data:

Fetching Tenant Data

Fetch tenant-specific configurations from API:

async function getSubdomainData(subdomain: string) {
  try {
    const res = await fetch(
      `my-api-domain/tenant/${subdomain}`
    );

    if (!res.ok) {
      throw new Error('Failed to fetch subdomain data');
    }

    return res.json();
  } catch (error) {
    console.error('Error fetching subdomain data:', error);
    return null;
  }
}

Testing and Debugging Subdomains

Local Testing

Test subdomains locally by editing your editing your url:

  • Add entries like tenant1.localhost:3000

Common Issues

  • CORS Errors: Ensure CORS policies allow subdomains.

  • DNS Misconfigurations: Verify wildcard DNS settings.


Conclusion

Managing subdomains in a Next.js multitenancy app is straightforward with dynamic routing and middleware. By leveraging subdomain routing, you can enhance tenant isolation, branding, and overall user experience. Whether you’re building a SaaS platform or a custom multitenant app, Next.js provides the tools to make implementation seamless.

This article outlines the implementation of a multitenant app using next js app router which is available from next versions 13 and above.
The approach emphasizes code readability and also security when handling multitenancy by separating the logic needed to get each tenants data from that which renders the data on the client side. The guide includes all code snippets and best practices for all steps in the managing of multitenancy and subdomain routing.

We’d love to hear your thoughts! Share your feedback, questions, or challenges in the comments. If you found this guide helpful, check out our other articles on CORS, Merge Conflicts and don’t forget to follow for more of insightful content.