You can support multitenancy in your application by providing separate data partitions for multiple client organizations, known as tenants. This allows you to customize data values for each tenant, while keeping the same data schema for all tenants. This makes provisioning new tenants more efficient because you don't have to change data structure when you add a tenant.
Benefits of multitenancy
Firestore in Datastore mode allows a multitenant application to use separated silos of data for each tenant while still using:
- a single project
- a single logical structure for the kinds
- a single set of index definitions, because the kinds are the same logically for each tenant
Datastore mode enables multitenancy by providing namespaces.
Multitenancy and partitioned data
Datastore mode uses partitions to silo data for each tenant. The combination of a project ID and a namespace ID forms a partition ID, which identifies each partition. An entity belongs to a single partition, and queries are scoped to a single partition.
Specifying a namespace for an entity
You specify the namespace when you create the entity: after you create the entity, you cannot change the namespace. If you don't explicitly specify a namespace for an entity, it is automatically assigned to the default namespace, which has no string identifier.
Using namespaces with parent entities
An entity and all of its ancestors belong to one and only one namespace. This means that when you create an entity with another entity designated as parent, the child entity is in the same namespace as its parent: you cannot specify some other namespace.
Sample use case
A key benefit of multitenancy is having the same application serve multiple
client organizations. To achieve this benefit, for a given kind, your
application should behave the same regardless of the namespace. For example,
from the application’s perspective, an entity of kind
Task in one namespace
should logically be the same as an entity of kind
Task in all other
namespaces. Your application could then use a single set of index definitions
Task queries, regardless of which namespaces contain
For example, consider a Task List application that silos data on a per user basis. The application could define namespaces based on user name, resulting in the following partitions:
Partition ID: project:"my_project_id"/namespace:"Joe" Partition ID: project:"my_project_id"/namespace:"Alice" Partition ID: project:"my_project_id"/namespace:"Charlie"
The application could define a logical structure of a
Task kind as follows, to
use for all namespaces:
kind: Task properties: - "done", Boolean - "created", DateTime - "description", String, excluded from index
When a user creates an entity of kind
Task, the entity is stored in the user’s
own partition, resulting in siloed data. The application processes
entities consistently across namespaces because only one schema is used for the
Task kind. An application with siloed data and consistent behaviour would be
If the logical structure of a
Task kind differs by namespace, the application
would not be multitenant because it processes
Task entities differently
across namespaces. For example, consider
Task kinds that have different schema
based on namespace:
Taskentities in the Joe namespace exclude the
descriptionproperty from the index
Taskentities in the Alice namespace include the
descriptionproperty from the index
The application could query on the
description property for Alice’s
entities, but it could not query on the
description property for Joe’s
entities, so the application would not be multitenant.
Viewing namespaces in the console
To see statistics for the namespaces used in your project, visit the Datastore Dashboard page in the Google Cloud Console. To programmatically determine which namespaces are used in your project, see Namespace queries.