Dartvel, home
Docs
Features
Studio
Cloud
Compared

Search the site

GitHub
pub.dev

GETTING STARTED

Getting started
Existing Flutter apps
Existing Native apps
Run on your phone

APP

UI and styling
Routing
State
Accessibility
Keyboard shortcuts
Localization
Devices and desktop
Native device access
Media, 3D and XR

DATA

Data models
Forms
Search
Sync and offline
Import and export
Change capture
Database
Cache
File storage
Images
Privacy and erasure

BACKEND

Backend functions
Auth and sessions
Authorization
Queues and jobs
Workers and memory
Notifications and mail
Outbound HTTP
AI
Webhooks
GraphQL and OpenAPI
API keys and OAuth
Multi-tenancy
Billing and commerce
Modules

OPERATIONS

Edge security
Secrets and environments
Monitoring
Releases

SHIPPING

Build targets
Telegram Mini Apps
Static web hosting
Servers and deploying

REFERENCE

Testing
CLI reference
Coding agents

BACKEND

Multi-tenancy

Serve many customers from one deployment, each seeing only their own data.

The tenant is resolved per request and carried into jobs and the cache.

ON THIS PAGE

Resolve the tenant from the request

Scope a model to the tenant

Read and switch the tenant in code

Keep tenants in separate schemas or databases

Query across tenants on purpose

Status

Resolve the tenant from the request

# pubspec.yaml
dartvel:
  tenancy:
    isolation: shared-database
    source: subdomain

Copy code to clipboard

source is subdomain, header, path-prefix or query-parameter.

header and queryParameter rename the header (x-tenant) and the parameter (tenant).

require: true refuses a request with no tenant.

Scope a model to the tenant

@DVModel(tenantScoped: true)
class const _Invoice({required final String id, required final int totalCents});

Copy code to clipboard

The table gets a dv_tenant column, and every read and write filters on it.

A tenant-scoped model cannot have public pages.

For a table with rows, run dartvel db migrate --tenant <id>.

Read and switch the tenant in code

final String tenant = DV.currentTenant; // 'default' when nothing resolved one

await DV.withTenant('acme', () async {
  // Tenant-scoped models, cache keys and dispatched jobs belong to acme here.
  final List<Invoice> invoices = await Invoice.all();
  DV.log('${invoices.length} invoices for acme');
});

Copy code to clipboard

A job dispatched inside withTenant runs in that tenant. Cache keys are prefixed with it.

Keep tenants in separate schemas or databases

shared-database

Keeps tenants apart by: A dv_tenant column on every scoped table, the default

schema-per-tenant

Keeps tenants apart by: A schema per tenant, chosen when each statement runs

database-per-tenant

Keeps tenants apart by: A database per tenant, opened once and kept

// Under isolation: database-per-tenant, each tenant opens its own database.
void configureTenantDatabases() {
  DV.Database.configureTenantDatabases(
    (String tenant) => SqliteDVDatabaseAdapter.file('tenants/$tenant.db'),
  );
}

Copy code to clipboard

database-per-tenant without configureTenantDatabases is refused, so no tenant falls back to a shared database.

A misspelled isolation or source fails the build.

Query across tenants on purpose

Planned

A generated cross-tenant data model query surface is not yet implemented. Application queries stay inside the tenant selected by DV.withTenant.

Group the people inside a tenant into an organization with roles and invitations. See Organizations.

Status

Partial

Spec section: Multi-tenancy

Planned work and implementation limits

schema-per-tenant and database-per-tenant do not create the schemas or databases.

Hosted search engines are not scoped by tenant.

database-per-tenant is opened by code you write. Configuration cannot select it yet.

PREVIOUS API keys and OAuth Let other systems call your app's API with scoped keys or OAuth
NEXT Billing and commerce Subscriptions, store purchases, tax and usage limits
GitHub
pub.dev
npm
Acknowledgements
Privacy
Terms

FSL-1.1-MIT licensed. Built with Dartvel.

Dartvel is made by

SigmaDev Digital

To the bottom