To create web applications and APIs that are multi-tenant, the application/API code needs to be multitenancy aware. The applications/APIs must be able to determine the tenant the request belongs to and ensure the appropriate database is used for transactions belonging to a particular tenant. This is especially true when using the horizontally partitioned deployment multi-tenancy model, as shown below.
In this model, the data is isolated into database per tenant and the front-end application and APIs are shared. As such the recommended approach is to use a Catalog to store mappings of tenants to databases, as shown below.
The applications/APIs consult the Catalog, get the tenant specific database connection information, and complete the operations as necessary using the appropriate tenant database.
To create a multi-tenant application or API, the below architecture is proposed.
-
App Service: This hosts the multi-tenant application or API code.
-
App Configuration Service: This contains all tenant configurations arranged in grouping by tenant identifier. See below for Keys pattern.
-
Key Vault: This stores all sensitive tenant information such as database connection information and other credentials.
Use Key prefixes to create groupings of configurations corresponding to each tenant. Name the settings in the format "<TenantName>:<Grouping>:<Key-Name>".
For example, given the tenants "Alpha, Bravo, Charlie", you can set up the "CustomerName" setting as below:
"Alpha:Settings:CustomerName"
"Bravo:Settings:CustomerName"
"Charlie:Settings:CustomerName"
And to setup the "OrderDBConnection" connection string:
"Alpha:ConnectionStrings:OrderDBConnection"
"Bravo:ConnectionStrings:OrderDBConnection"
"Charlie:ConnectionStrings:OrderDBConnection"
Following are the high-level steps for setting up the demo application:
- Create the services, Azure App Service, Azure App Configuration Service, and Azure Key Vault service.
- Configure the Services.
- Test the API by sending requests simulating multiple tenants.
-
Deploy demo by running the below steps.
# Optional if already logged in az login # Check to deploy to correct subscription az account show git clone https://github.com/fsaleemm/Multi-Tenant-WebAPI.git cd Multi-Tenant-WebAPI az deployment sub create --name "<unique deployment name>" --location "<Your Chosen Location>" --template-file infra/main.bicep --parameters name="<Name suffix for resources>"
-
Test the deployed API by sending requests simulating different tenants.
- Alpha tenant request, replace the <Your-Site-Name> with the name of your web site.
curl -X POST https://<Your-Site-Name>.azurewebsites.net/api/Configs/Customer/Order -H 'Content-Type: application/json' -d '{ "Date" : "2022-04-14", "OrderId" : 1, "TenantId" : "Alpha", "OrderDetail" : "Create Alpha Work Order" }'
if using PowerShell use the following:
(Invoke-WebRequest -Uri "https://<Your-Site-Name>.azurewebsites.net/api/Configs/Customer/Order" -Headers @{'Content-Type' = 'application/json'} -Method 'POST' -Body '{ "Date" : "2022-04-14", "OrderId" : 1, "TenantId" : "Alpha", "OrderDetail" : "Create Alpha Work Order" }').Content
Response "connectionString" should be for Alpha tenant:
{ "status": "Submitted for customer id : 101", "orderDetail": "Create Alpha Work Order", "customerName": "Customer Alpha", "connectionString": "Server=ABC,1433;InitialCatalog=AlphaDatabase;UsingKeyVault=true" }
- Bravo tenant request
curl -X POST https://<Your-Site-Name>.azurewebsites.net/api/Configs/Customer/Order -H 'Content-Type: application/json' -d '{ "Date" : "2022-04-14", "OrderId" : 1, "TenantId" : "Bravo", "OrderDetail" : "Create Bravo Work Order" }'
Response "connectionString" should be for Bravo tenant:
{ "status": "Submitted for customer id : 102", "orderDetail": "Create Bravo Work Order", "customerName": "Customer Bravo", "connectionString": "Server=ABC,1433;InitialCatalog=BravoDatabase;UsingKeyVault=true" }
The code and deployment biceps are for demonstration purposes only.