0

I'm creating a multi-tenant SaaS product that is comprised of pretty basic PHP / MySQL connections. I'm using single database and mapping all tenants to a tenantId column in the database.

I'm also using subdomains to give each tenant a front-end / back-end of the software, so upon registering, they'll be given something like company1.mysaas.dev. Their subdomain is also mapped to their tenantId in the database.

All tenant's domains are pointed to a single code base / the same entry point of the app, so when visiting their domain it needs to map to their config file / connect to their tenantId in the database to load only their files.

Here's what I'm doing on my index.php file that each subdomain is loading:

// Connect to Database
$db = new mysqli('localhost', 'root', '', 'saas');

// Check Connection
if ($db->connect_error) {
  die("Connection failed: " . $db->connect_error);
}

$domain = $_SERVER['HTTP_HOST']; // Get current domain
$stmt = $db->prepare('SELECT `tenant_id` FROM `domains` WHERE domain = ?');
$stmt->bind_param('s', $domain);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();

$tenantId = $row['tenant_id']; // Get Tenant ID based on current domain

if ($tenantId) {
  $serverConfig = "tenants/$tenantId/config.php";
  if (file_exists($serverConfig)){
    return require($serverConfig);
  } else {
    header("Location: https://mysaas.dev"); // No config file found, redirect back to app homepage
  }
} else {
  header("Location: https://mysaas.dev"); // No tenant ID found, redirect back to app homepage
}

For the most part, the above is working as I test just a hand full of subdomains.

My question is if this is a proper way of handling incoming requests based on subdomains or is there a more efficient way when scaling this up?

What if there's a thousand people hitting the entry point of the app all from different tenant's subdomains, will this scale?

Dharman
  • 30,962
  • 25
  • 85
  • 135
AJK
  • 391
  • 9
  • 27
  • 1
    please use always **prepared statements with parameters** to **prevent sql injection** see https://stackoverflow.com/questions/60174/how-can-i-prevent-sql-injection-in-php – nbk Sep 04 '20 at 14:35
  • @nbk Thanks! Yeah I'll have to refine my code here for production to handle errors and security. Mostly just trying to see if this would be a scalable way to handle connections from incoming requests across all tenants. – AJK Sep 04 '20 at 14:38
  • check the examples am php.com to see how you can check if a result was given. , you have to redisign your code, so you can add this also – nbk Sep 04 '20 at 14:39
  • 1
    If you're going to use the database connection after this, I would recommend that you don't close it. Doing that would require you to create a new connection, which isn't as effective as reusing the same. Regarding scaling, this can be scaled depending on the infrastructure behind it (servers/load balancers etc). Take that part when you see it's needed. – M. Eriksson Sep 04 '20 at 14:40
  • 1
    There is nothing inherently unscalable about this design, I've run tens of thousands of subdomains through a single app in this manner. However, I would not put per-tenant config into separate PHP source files like this. Instead consider putting the config in the database. You don't want to have to write, lint, and deploy code every time you add a tenant. – Alex Howansky Sep 04 '20 at 15:09
  • Thanks for the suggestion guys, sounds like I can continue on with this method. @AlexHowansky didn't really consider that, Thanks! – AJK Sep 04 '20 at 15:16

0 Answers0