Building a SaaS Application with Laravel, Stancl Tenancy, and Next.js
In most of my projects, I prefer using Laravel for the backend and Next.js for the frontend. In a recent SaaS project, I developed a multi-tenant platform that allowed users to create their own tenants, each accessible via a unique subdomain. One of the key challenges was implementing an efficient method to handle incoming requests on a per-tenant basis.
The setup was structured as follows:
The Next.js frontend was hosted on Vercel. The root domain (example.com
) pointed to a landing page (which could be a static HTML page or a WordPress site), while all wildcard subdomains (e.g., *.example.com
) were routed to the Next.js application.
The Laravel backend was hosted on a separate subdomain (api.example.com
). This subdomain was excluded from the wildcard DNS rule and configured to point directly to the backend server.
P.S. in the project I needed to reserve more sub domains, but anyway, this is not the point of the post.
With that setup, the Next.js app intercepted all incoming requests, extracted the tenant subdomain, and forwarded it to the API as a custom header (X-Tenant
) for verification.
While the Stancl tenancy package offers several tenant resolution strategies, the most practical solution in our case was to rely on the X-Tenant
header for consistent and centralized tenant identification. So none of the provided methods was enough for us. However, we could simply extend the class InitializeTenancyByDomainOrSubdomain
like this:
<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Illuminate\Http\Request;
use Stancl\Tenancy\Middleware\InitializeTenancyByDomainOrSubdomain;
class InitializeTenancyByDomainHeader extends InitializeTenancyByDomainOrSubdomain
{
public function getDomain(Request $request): string
{
return $request->header('X-Tenant', '');
}
}
Essentially, we override the getDomain
method to retrieve the tenant domain from the custom X-Tenant
request header instead of relying on the URL.
Forwarding the Tenant Header from Next.js
Now, in the Next.js app, we need to extract the domain being visited and send it to the backend as the X-Tenant
header for tenant verification. Here’s a simplified example you could include in your root layout or middleware logic:
const tenant = await getTenant()
if (!tenant ) {
notFound();
}
The getTenant
function reads the request headers, extracts the host
, and sets it as the X-Tenant
value when making an API call to a route protected by the InitializeTenancyByDomainHeader
middleware. That route will tell your NextJS app if that domain is for an existing tenant or simply, 404