Skip to main content
Version: 4.xx.xx

Multitenancy
Check the guide
Please check the guide for more information on this topic.

Refine's Enterprise Edition provides built-in support for Multitenancy. This feature allows you to build applications that can serve multiple tenants with a single codebase with help of pre-built components and hooks with minimal configuration.

Installation

This package is included in Refine's Enterprise Edition. To learn more about Refine's Enterprise Edition, please contact us.

npm i @refinedev-ee/enterprise @refinedev-ee/multitenancy
.npmrc
# A registry with the auth token should be added for the @refinedev-ee scope
@refinedev-ee:registry=https://registry.npmjs.org/
//registry.npmjs.org/:_authToken=$NPM_TOKEN

Usage

To use the multitenancy feature, we need to wrap our application with the <RefineEnterprise /> component and provide the multitenancyProvider prop.

import { RefineEnterprise } from "@refinedev-ee/enterprise";
import { useRouterAdapter, WithTenant } from "@refinedev-ee/multitenancy";

type Tenant = {
id: string;
name: string;
};

// ... other imports

const App = () => {
return (
<RefineEnterprise
// ... other props
multitenancyProvider={{
adapter: useRouterAdapter(),
fetchTenants: async () => {
const response = await dataProvider("<API_URL>").getList<Tenant>({
resource: "tenants",
pagination: {
mode: "off",
},
});
const tenants = response.data;
const defaultTenant = tenants[0];

return {
tenants,
defaultTenant,
};
},
}}
>
<WithTenant
fallback={<div>Tenant not found</div>}
loadingComponent={<div>Loading...</div>}
>
{/* Your app code */}
</WithTenant>
</RefineEnterprise>
);
};

multitenancyProvider

The multitenancyProvider is a prop that accepts an object with two properties: adapter and fetchTenants.

fetchTenants

The fetchTenants function is a crucial part of the multitenancyProvider, responsible for fetching tenant data from an API or data source and determining the default tenant for the app. The function should return an object with two properties: tenants and defaultTenant.

adapters

Adapters define where tenant information is stored. Refine offers useRouterAdapter for storing tenants in the URL and useLocalStorageAdapter for local storage.

useRouterAdapter

Extracts the tenantId from the URL and updates the route when the tenant changes.

import { useRouterAdapter } from "@refinedev-ee/multitenancy";

const multitenancyProvider = {
adapter: useRouterAdapter({
// The parameter name to use in the URL. (eg: localhost:3000/:tenantId/products)
parameterName: "tenantId",
// The tenant field to use in the route params. (eg: localhost:3000/:tenantId/products)
parameterKey: "id",
// Determines if the query string should be used to get the tenant instead of the route params. (eg: localhost:3000/products?tenantId=1)
useQueryString: false,
}),
fetchTenants: async () => {
// Fetch tenants from the API
},
};

useLocalStorageAdapter

Retrieves tenantId from local storage and updates it when the tenant changes.

import { useLocalStorageAdapter } from "@refinedev-ee/multitenancy";

const multitenancyProvider = {
adapter: useLocalStorageAdapter({
// The key to use in the local storage. (eg: localStorage.getItem(key))
storageKey: "tenantId",
}),
fetchTenants: async () => {
// Fetch tenants from the API
},
};

Components

WithTenant

The <WithTenant /> component is required to wrap your app code. It fetches tenants, handling the loading state and error state.

import { RefineEnterprise } from "@refinedev-ee/enterprise";
import { WithTenant } from "@refinedev-ee/multitenancy";

<WithTenant
// render a component when the tenant is not available.
fallback={<div>Tenant not found</div>}
// render a component while the tenant is loading.
loadingComponent={<div>Loading...</div>}
>
{/* Your app code */}
</WithTenant>;

Tenant Select components

These components allow users to select a tenant from a list of available tenants. They are automatically change the current tenant when a new tenant is selected from the list.

import { TenantSelect } from "@refinedev-ee/multitenancy/antd";

<TenantSelect
// Specifies the tenant object field to display in the select component.
optionLabel="title"
// Specifies the tenant object field to use as the value in the select component.
optionValue="id"
// Event handler for when a tenant is selected, receiving the selected tenant as an argument.
onChange={(tenant) => console.log(tenant)}
// Function to sort tenants.
sortTenants={(a, b) => a.name.localeCompare(b.name)}
/>;

Hooks

Refine provides hooks to interact with the multitenancy feature.

useMultitenancy

The useMultitenancy hook is used to interact with the multitenancy context.

import { useMultitenancy } from "@refinedev-ee/multitenancy";

const {
// The current tenant object.
tenant,
// The list of available tenants.
tenants,
// The loading state of the tenants.
isLoading,
// It triggers `authProvider.fetchTenants` to fetch tenants.
fetchTenants,
// It sets the current tenant. It accepts a tenant object as an argument.
setTenant,
// It deletes the current tenant.
deleteTenant,
} = useMultitenancy();

Handling Multi-tenant Requests in Data Providers

Refine automatically sends the tenantId to the data provider in the meta object. You can access the tenantId in the data provider and use it to fetch tenant-specific data.

To customize the data providers, you can override each method in the data provider instance or use the swizzle command to be fully able to customize the data provider for your needs.

An example implementation of a custom getList method is shown below.

import dataProvider from "@refinedev/simple-rest";

const API_URL = "<API_URL>";
const baseDataProvider = dataProvider(API_URL);

const customDataProvider = {
...baseDataProvider,
getList: async ({ resource, filters = [], meta, ...props }) => {
const { tenantId } = meta;

// We're adding the tenantId to the filters
// Your API may have a different way of handling this
if (meta?.tenantId) {
filters.push({
field: "organization",
operator: "eq",
value: meta.tenantId,
});
}

// Call the base data provider's getList method with the updated filters
return baseDataProvider.getList({
resource,
filters,
meta,
...props,
});
},
};

Examples

Here are some examples of multi-tenant apps built with Refine Enterprise Edition: