Hello, laravel developers! In this article, I will walk you through how to build a multi-tenant SaaS application in Laravel 11. Each tenant will have its own subdomain, and we’ll implement database isolation to keep tenant data separate.
We will also set up custom middleware and use Laravel’s routing system to handle tenant subdomains effectively. By the end of this guide, you'll have a solid foundation for building scalable, multi-tenant applications.
Building a Multi-Tenant SaaS App in Laravel 11
First, let’s create a new Laravel project:
composer create-project laravel/laravel multi-tenant-saas
To support subdomain routing, open the routes/web.php
file and update the routing structure to include the subdomains. Laravel’s domain
method will allow us to capture subdomain routes.
use Illuminate\Support\Facades\Route;
Route::domain('{tenant}.yourapp.com')->group(function () {
Route::get('/', function ($tenant) {
return "Welcome to {$tenant}'s application!";
});
});
In this example, {tenant}
is a placeholder for the subdomain (tenant name).
To isolate tenants by subdomain, we need middleware that identifies the tenant based on the subdomain. Let’s create a new middleware:
php artisan make:middleware IdentifyTenant
Inside app/Http/Middleware/IdentifyTenant.php
, write logic to extract the tenant and set the correct database connection:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Config;
class IdentifyTenant
{
public function handle($request, Closure $next)
{
$tenant = $request->route('tenant');
// Set dynamic database connection based on tenant
$this->setTenantDatabase($tenant);
return $next($request);
}
protected function setTenantDatabase($tenant)
{
$connection = [
'driver' => 'mysql',
'host' => '127.0.0.1',
'database' => 'tenant_' . $tenant,
'username' => 'root',
'password' => 'password',
];
Config::set('database.connections.tenant', $connection);
\DB::setDefaultConnection('tenant');
}
}
This middleware dynamically switches the database based on the tenant's subdomain.
Next, register the IdentifyTenant
middleware in bootstrap/app.php:
<?php
use Illuminate\Foundation\Application;
use Illuminate\Foundation\Configuration\Exceptions;
use Illuminate\Foundation\Configuration\Middleware;
return Application::configure(basePath: dirname(__DIR__))
->withRouting(
web: __DIR__.'/../routes/web.php',
commands: __DIR__.'/../routes/console.php',
health: '/up',
)
->withMiddleware(function (Middleware $middleware) {
$middleware->alias([
'identifyTenant' => \App\Http\Middleware\IdentifyTenant::class,
]);
})
->withExceptions(function (Exceptions $exceptions) {
//
})->create();
Now, apply this middleware to your tenant routes in routes/web.php:
Route::domain('{tenant}.yourapp.com')
->middleware('identifyTenant')
->group(function () {
Route::get('/', function ($tenant) {
return "Welcome to {$tenant}'s application!";
});
});
Create databases for each tenant. For example, for a tenant named "tenant1", the database would be named tenant_tenant1.
To handle migrations per tenant, you can update the database.php
configuration or use a separate migration command per tenant:
php artisan migrate --database=tenant
To quickly seed tenant-specific data, create a seeder that runs based on the tenant’s subdomain.
php artisan make:seeder TenantSeeder
In database/seeders/TenantSeeder.php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class TenantSeeder extends Seeder
{
public function run()
{
\DB::table('users')->insert([
'name' => 'Tenant User',
'email' => '[email protected]',
'password' => bcrypt('password'),
]);
}
}
Run the seeder for each tenant:
php artisan db:seed --class=TenantSeeder --database=tenant
Once everything is set up, test the application by visiting tenant subdomains:
tenant1.yourapp.com
tenant2.yourapp.com
Each subdomain will connect to its own database and display tenant-specific content.
You might also like:
Moving data from one table to another is a common task when managing databases in Laravel applications. Laravel provides...
Dec-26-2024
In this article, we will see laravel 9 authentication using inertia js. Here, you can learn how to authenticat...
Dec-05-2022
In this post, I will show you how to break lines for Textarea in laravel blade. Many times when you save...
Jul-26-2020
In this article, we will see laravel 8 socialite login with a google account. This post gives you an example of a larave...
Dec-01-2020