Kritim Yantra
Jun 09, 2025
Multi-tenancy allows a single application to serve multiple customers (tenants) while keeping their data isolated. Common approaches:
tenant_id
column) In this guide, we’ll use Shared Database with Tenant ID for simplicity.
composer create-project laravel/laravel:^12.0 multi-tenant-app
cd multi-tenant-app
npm install react react-dom @vitejs/plugin-react
Update vite.config.js
:
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
laravel(['resources/js/app.jsx']),
react(),
],
});
We’ll use Laravel’s global scopes and middleware to isolate tenant data.
tenant_id
to Users & Tenant-Specific Modelsphp artisan make:migration add_tenant_id_to_users_table
// Migration
public function up() {
Schema::table('users', function (Blueprint $table) {
$table->foreignId('tenant_id')->constrained('tenants')->onDelete('cascade');
});
}
Tenant
Modelphp artisan make:model Tenant -m
// Migration
Schema::create('tenants', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('domain')->unique();
$table->timestamps();
});
// app/Models/Tenant.php
use Illuminate\Database\Eloquent\Builder;
protected static function booted() {
static::addGlobalScope('tenant', function (Builder $builder) {
if (auth()->check()) {
$builder->where('tenant_id', auth()->user()->tenant_id);
}
});
}
php artisan make:middleware TenantMiddleware
// app/Http/Middleware/TenantMiddleware.php
public function handle($request, Closure $next) {
if ($tenant = Tenant::where('domain', $request->getHost())->first()) {
config(['tenant' => $tenant]);
return $next($request);
}
abort(404, 'Tenant not found.');
}
Register in app/Http/Kernel.php
:
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\TenantMiddleware::class,
],
];
Modify LoginController
to ensure users log in only to their tenant:
protected function attemptLogin(Request $request) {
return Auth::attempt([
'email' => $request->email,
'password' => $request->password,
'tenant_id' => Tenant::where('domain', $request->getHost())->first()->id,
]);
}
// resources/js/components/TenantDashboard.jsx
import { useEffect, useState } from 'react';
import axios from 'axios';
export default function TenantDashboard() {
const [data, setData] = useState([]);
useEffect(() => {
axios.get('/api/tenant-data').then((res) => {
setData(res.data);
});
}, []);
return (
<div>
<h1>Tenant Dashboard</h1>
{data.map((item) => (
<div key={item.id}>{item.name}</div>
))}
</div>
);
}
Use React Router to handle tenant-specific routes:
// resources/js/routes/AppRouter.jsx
import { Routes, Route } from 'react-router-dom';
export default function AppRouter() {
return (
<Routes>
<Route path="/dashboard" element={<TenantDashboard />} />
<Route path="/settings" element={<TenantSettings />} />
</Routes>
);
}
// resources/js/components/TenantSwitcher.jsx
export default function TenantSwitcher() {
const [tenants, setTenants] = useState([]);
useEffect(() => {
axios.get('/api/user/tenants').then((res) => {
setTenants(res.data);
});
}, []);
return (
<select onChange={(e) => window.location.href = `https://${e.target.value}.yourdomain.com`}>
{tenants.map((tenant) => (
<option key={tenant.id} value={tenant.domain}>{tenant.name}</option>
))}
</select>
);
}
Tenant::create(['name' => 'Tenant A', 'domain' => 'tenant-a.local']);
Tenant::create(['name' => 'Tenant B', 'domain' => 'tenant-b.local']);
tenant-a.local
→ Only see Tenant A’s data. tenant-b.local
→ Only see Tenant B’s data.✅ Use Subdomains (tenant1.yourdomain.com
) for easy routing.
✅ Cache Tenant Data to avoid repeated DB queries.
✅ Backup Tenant Databases Separately if using DB-per-tenant.
✅ Use Queues for Tenant-Aware Jobs (Laravel’s Queue::tenant()
).
You’ve built a multi-tenant SaaS app with:
Transform from beginner to Laravel expert with our personalized Coaching Class starting June 20, 2025. Limited enrollment ensures focused attention.
1-hour personalized coaching
Build portfolio applications
Industry-standard techniques
Interview prep & job guidance
Complete your application to secure your spot
Thank you for your interest in our Laravel mentorship program. We'll contact you within 24 hours with next steps.
No comments yet. Be the first to comment!
Please log in to post a comment:
Sign in with Google