Child database access
A multi-tenant database architecture allows a single database to serve multiple tenants or customers. Typically, database systems that provide this feature have multiple databases each with their own access control. Fauna extends this multitenancy capability by allowing you to create parent-child hierarchies in a single Fauna database.
Using the multitenancy feature, a single database in your organization can serve multiple teams through child databases. As a tenant, a child database has schemas independent from the parent. A team can create collections, documents, indexes, and even other child databases in their child without impacting the parent or other child databases.
By creating access keys on the child databases, teams have dedicated access
keys for their own child database. When connected to a child database, it is
not possible to access the parent database, other child databases, or even to
discover that these exist. A parent database admin
key holder can revoke a
child access key at any time. A parent database server
key holder cannot
manage the child databases.
Create child databases
This procedure creates two child databases in an existing CoffeeStore
parent
database.
-
Log in to the Dashboard.
-
Click the
CoffeeStore
database to open it and reveal the Fauna Shell. -
Ensure you have an existing
coffee-admin
andcoffee-server
key by listing the keys.The
coffee-admin
key manages theCoffeeStore
parent and any child databases it might have. If thecoffee-admin
key does not exist, create it but remember to save the secret in a safe place. The Get started explains how to setup for the example. -
Create two child databases, one called
production
and one calledinternal
.[ { name: "production", coll: Database, ts: Time("2023-07-01T22:38:03.270Z"), global_id: "ywxwmh6o4ybng" }, { name: "internal", coll: Database, ts: Time("2023-07-01T22:38:03.270Z"), global_id: "ywxwmh6ohybng" } ]
-
Click Resources in the left navigation panel an expand the Databases list. You should see two nodes, one for each child:
You can also list the databases using the
Database.all()
query.
Create admin
keys for child databases
The admin
keys on a parent database can manage the child databases. To allow
users to manage a child database, create a key with the admin
role on that
child database.
-
Make sure your Shell is open on the CoffeeStore database, this is the parent database.
-
Create
admin
secrets for each child database.[ { id: "369091607961535011", coll: Key, ts: Time("2023-07-01T22:53:57.030Z"), role: "admin", database: "production", secret: "fnAFH0bcpiACI-pzd2WhCxKlFCZqHf7ZJWZ9YQ54" }, { id: "369091607961536035", coll: Key, ts: Time("2023-07-01T22:53:57.030Z"), secret: "fnAFH0bcpiAGI8Wu5OAA2cKMv6vkxc5MFwvGekNp", role: "admin", database: "internal" } ]
At creation is the only time Fauna displays the
secret
field. The value in this field is equivalent to a password. Fauna cannot recover a secret that is discarded or lost. Copy and save the secret to a password manager or other safe location. Delete and replace keys or tokens for which you have lost the secret. If you no longer need a key or token, you should delete it. -
Take a moment to save the secrets for these new keys. You need them in the next procedure.
-
To find all the keys that are for a child database, list keys that have a
database
field.This is an efficient query for long lists of keys. Only child database keys have a
database
field. -
Navigate to the CoffeeStore/production database by selecting it from the Database tree.
-
Query for a list of keys in the
production
database{ data: [] }
The list in the child database is empty. The keys you created in the parent database are on the parent, they are not visible from the child.
Add internal
child databases
The internal
team wants two child databases, named personnel
and
bulletinBoard
. The team also has an application for each of its child
databases.
Keys with the server
role are equivalent to the admin
role with some
exceptions. Child databases, keys, tokens,
and their associated documents cannot be directly managed with server
role
privileges. A server
key can be used to connect to their associated
database. The internal team applications can use server
keys to connect to
the personnel
and bulletinBoard
databases and manage non-native collections.
Create two child databases and a server key for each child database.
-
Navigate to the CoffeeStore/internal database by selecting it from the Database tree.
-
Set the run menu to Secret.
-
Enter the CoffeeStore/internal
admin
secret you created in the last procedure. -
Create the two child databases:
[{name: "personnel"}, {name: "bulletinBoard"}].map(Database.create) [ { name: "personnel", coll: Database, ts: Time("2023-07-02T18:16:35.780Z"), global_id: "ywxa1ahhaydng" }, { name: "bulletinBoard", coll: Database, ts: Time("2023-07-02T18:16:35.780Z"), global_id: "ywxa1ahh4ybng" } ]
-
Create
server
keys for use in each of these new child databases.[ { id: "369164880539813411", coll: Key, ts: Time("2023-07-02T18:18:35.130Z"), role: "server", database: "personnel", secret: "fnAFH4mAwCAGI_aCpNCqX5WWf3f-Y5fWXI9XSizX" }, { id: "369164880539812387", coll: Key, ts: Time("2023-07-02T18:18:35.130Z"), secret: "fnAFH4mAwCACI8zdIiQRKDGeNStRFLDttYI56d3w", database: "bulletinBoard", role: "server" } ]
-
Save copies of the secrets for use later in this cookbook.
At creation is the only time Fauna displays the
secret
field. The value in this field is equivalent to a password. Fauna cannot recover a secret that is discarded or lost. Copy and save the secret to a password manager or other safe location. Delete and replace keys or tokens for which you have lost the secret. If you no longer need a key or token, you should delete it.
Access a child database from an application
If you have completed the previous section of the cookbook,
Keys and built-in roles, you should already have the coffeestore.mjs
file application available in your environment.
-
Edit the
coffeestore.mjs
file and replace thetry-catch
block with this one:try { // build queries using the fql function const collection_query = fql`Collection.create({ name: "Post" })`; // Execute the query, return more informative decorated format // The decorated format is not suitable for complex objects in production systems const collection_result = await client.query(collection_query, {format: "decorated"}); console.log(collection_result); console.log(collection_result); // define some data in your app const post = { headline: "Lost Doggo" }; // query using your application local variables const document_query = fql` Post.create(${post}) { id, ts, headline } `; const document_result = await client.query(document_query); console.log(document_result); } catch (error) { console.log(error) }
-
Set the
FAUNA_SECRET
environment variable to theinternal/bulletinBoard
server
secret:export FAUNA_SECRET=<savedSecret>
-
Run the application:
node coffeestore.mjs
You should see the
Post
document response:{ data: '{\n' + ' name: "Post",\n' + ' coll: Collection,\n' + ' ts: Time("2023-08-09T23:13:53.400Z"),\n' + ' indexes: {},\n' + ' constraints: []\n' + '}', static_type: 'CollectionDef', summary: '', txn_ts: 1691680909210000, stats: { compute_ops: 1, read_ops: 0, write_ops: 1, query_time_ms: 95, contention_retries: 0, storage_bytes_read: 1569, storage_bytes_write: 353, rate_limits_hit: [] }, schema_version: 1691676820650000 } { data: { id: '372626144347815969', ts: TimeStub { isoString: '2023-08-09T23:13:53.520Z' }, headline: 'Lost Doggo' }, static_type: '{ headline: Any, ts: Any, id: Any }', summary: '', txn_ts: 1691622833520000, stats: { compute_ops: 1, read_ops: 0, write_ops: 1, query_time_ms: 71, contention_retries: 0, storage_bytes_read: 0, storage_bytes_write: 213, rate_limits_hit: [] }, schema_version: 1691622833400000 }
-
In the Dashboard, navigate to your new
Post
collection.
Is this article helpful?
Tell Fauna how the article can be improved:
Visit Fauna's forums
or email docs@fauna.com
Thank you for your feedback!