Multitenancy

Many database systems provide multitenant capabilities, which can have multiple databases, each with its own access controls. Fauna takes this further by allowing any database to have multiple child databases. This allows you to create a child database in the parent database, create an admin key for the child database, and provide the key to a team that needs a database for application development. This gives the team full access to the child database to create collections, documents, and indexes, other child databases in their database, and manage them as needed without requiring parent database owner intervention.

When connected to a child database, it isn’t possible to access the parent database or even discover that there is a parent database. As the parent database owner, you can revoke the access key at any time.

To query another database or submit queries involving writes to a child database, you must use the secret associated with that database.

This tutorial shows you how to set up a hierarchy of databases, starting with two top-level, broadly scoped databases and continuing down to individual databases for the internal teams. You’ll also learn how to create access secrets.

Step 1: Start

Log in to the Dashboard and create a parent database or select an existing database as the parent database.

You must have admin permission to create and manage child databases.

Step 2: Create the top-level databases

Create a set of top-level databases named production and internal that can be allocated to individual teams:

[{name: "production"}, {name: "internal"}].map(Database.create)
[
  {
    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"
  }
]

Step 3: Create administrator secrets

Create admin secrets, which can be given to each team for full control over their databases but not the databases of other teams.

["production", "internal"].map(db => Key.create({role: "admin", database: db}))
[
  {
    id: "369091607961535011",
    coll: Key,
    ts: Time("2023-07-01T22:53:57.030Z"),
    role: "admin",
    database: "production",
    secret: "fnAFH0bcpiACI-pzd2JpnXxKlFCZqHf7ZJWZ9YQ54"
  },
  {
    id: "369091607961536035",
    coll: Key,
    ts: Time("2023-07-01T22:53:57.030Z"),
    secret: "fnAFH0bcpiAGI8Wu5OAA2cJpnXvkxc5MFwvGekNp",
    role: "admin",
    database: "internal"
  }
]

Make sure you save a copy of each secret. Secrets are only displayed when they’re created. If you lose a secret, you must delete the associated key and create a new one.

Step 4: Create per-team child databases

Given the new keys, each team can create its own child databases as needed. In this example, the production team doesn’t need child databases, but the internal team wants two child databases, named personnel and bulletinBoard.

  1. Select the internal database.

  2. Create the internal team child databases:

    [{name: "personnel"}, {name: "bulletinBoard"}].map(Database.create)
    [{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"
      }
    ]

Step 5: Create server secrets for the internal team

Server secrets don’t have permission to create or manage child databases. They can only be used to connect to their associated database.

The internal team has an application for each database, so create server secrets that permit internal team applications to connect to their databases:

["personnel", "bulletinBoard"].map(db => Key.create({role: "server", database: db}))
[
  {
    id: "369164880539813411",
    coll: Key,
    ts: Time("2023-07-02T18:18:35.130Z"),
    role: "server",
    database: "personnel",
    secret: "fnAFH4mAwCAGI_aCpNJpnXWWf3f-Y5fWXI9XSizX"
  },
  {
    id: "369164880539812387",
    coll: Key,
    ts: Time("2023-07-02T18:18:35.130Z"),
    secret: "fnAFH4mAwCACI8zdJpnXKDGeNStRFLDttYI56d3w",
    database: "bulletinBoard",
    role: "server"
  }
]

Make sure you save a copy of each secret. Secrets are only displayed when they’re created. If you lose a secret, you must delete the associated key and create a new one.

Step 6: Verify database visibility

  1. In the terminal with Fauna Shell connected to the internal database, run:

    Database.all()
    {
      data: [
        {
          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"
        }
      ]
    }
  2. In the terminal with the Fauna Shell connected to the top-level parent database, run:

    Database.all()
    {
      data: [
        {
          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"
        }
      ]
    }

Fauna lists only the immediate child databases in the connected database but you can use the admin secret to connect to the child databases and discover all databases in the hierarchy.

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!