Building a Multi-Tenant SaaS App in Laravel 11

Websolutionstuff | Oct-14-2024 | Categories : Laravel

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

Building a Multi-Tenant SaaS App in Laravel 11

 

Step 1: Set Up Your Laravel Application

First, let’s create a new Laravel project:

composer create-project laravel/laravel multi-tenant-saas

 

Step 2: Configure Subdomain Routing

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).

 

Step 3: Middleware for Tenant Identification

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.

 

Step 4: Register Middleware

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!";
        });
    });

 

Step 5: Configure Database for Each Tenant

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

 

Step 6: Create a Seeder for Tenants

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

 

Step 7: Test Your Application

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:

Recommended Post
Featured Post
Laravel 11 Move Data from One Table to Another Table
Laravel 11 Move Data from One...

Moving data from one table to another is a common task when managing databases in Laravel applications. Laravel provides...

Read More

Dec-26-2024

Laravel 9 Authentication Using Inertia JS
Laravel 9 Authentication Using...

In this article, we will see laravel 9 authentication using inertia js. Here, you can learn how to authenticat...

Read More

Dec-05-2022

Line Breaks In Laravel Blade
Line Breaks In Laravel Blade

In this post, I will show you how to break lines for Textarea in laravel blade. Many times when you save...

Read More

Jul-26-2020

Laravel 8 Socialite Login with Google Account
Laravel 8 Socialite Login with...

In this article, we will see laravel 8 socialite login with a google account. This post gives you an example of a larave...

Read More

Dec-01-2020