forked from refinedev/refine
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(docs): update Multitenancy documentation (refinedev#6436)
- Loading branch information
1 parent
493af2f
commit 66a1261
Showing
73 changed files
with
800 additions
and
5,006 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
264 changes: 264 additions & 0 deletions
264
documentation/docs/enterprise-edition/multitenancy/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
--- | ||
title: Multitenancy | ||
guide: true | ||
--- | ||
|
||
# Multitenancy <GuideBadge id="guides-concepts/multitenancy" /> | ||
|
||
Refine's Enterprise Edition provides built-in support for [Multitenancy](/docs/guides-concepts/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](https://s.refine.dev/okta-enterprise). | ||
|
||
<InstallPackagesCommand args="@refinedev-ee/enterprise @refinedev-ee/multitenancy"> | ||
|
||
```yml title=".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 | ||
``` | ||
|
||
</InstallPackagesCommand> | ||
|
||
## Usage | ||
|
||
To use the multitenancy feature, we need to wrap our application with the `<RefineEnterprise />` component and provide the `multitenancyProvider` prop. | ||
|
||
```tsx | ||
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. | ||
|
||
```tsx | ||
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. | ||
|
||
```tsx | ||
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. | ||
|
||
```tsx | ||
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. | ||
|
||
<Tabs wrapContent={false}> | ||
|
||
<TabItem value="Ant Design"> | ||
|
||
```tsx | ||
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)} | ||
/>; | ||
``` | ||
|
||
</TabItem> | ||
|
||
<TabItem value="Material UI"> | ||
|
||
```tsx | ||
import { TenantSelect } from "@refinedev-ee/multitenancy/mui"; | ||
|
||
<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)} | ||
/>; | ||
``` | ||
|
||
</TabItem> | ||
|
||
</Tabs> | ||
|
||
### Hooks | ||
|
||
Refine provides hooks to interact with the multitenancy feature. | ||
|
||
#### useMultitenancy | ||
|
||
The `useMultitenancy` hook is used to interact with the multitenancy context. | ||
|
||
```tsx | ||
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`](/docs/packages/cli/#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. | ||
|
||
```ts | ||
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](https://refine.dev/enterprise/): | ||
|
||
- [Multitenancy App with Strapi](https://refine.dev/templates/multitenancy-strapi/) | ||
- [Isolated Multitenancy App with Rest API](https://multitenancy-isolated.netlify.app/) |
90 changes: 0 additions & 90 deletions
90
documentation/docs/guides-concepts/multi-tenancy/examples/selector.tsx
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.