This is a blog post based on a request related to a Github issue. So here it comes! :)

NHost.io related, Predictable pricing and Multi Tenant · Issue #82 · elitan/hasura-backend-plus
Hi, Could I request a similar guide like below, to able to host dedicated server on NHost.io for every 1k customer etc. Multi Tenant

The blog post also will be inspired by a blog post written about multi-tenant apps using Prisma.

Multi-tenant application

What is it and why does it matter?

A multi-tenant application is an application that support multiple tenants, usually customers, where each customer has an isolated environment in your system.

If you are building a SaaS app you must likely are building a multi-tenant application where each of your customer has its own login and can access its own data. But you share resources such as frontend, backend and database to keep costs low, maintenance easy and development fast.

So let's see how we could do this with Nhost and Hasura

We will build a Customer Relationship Management (CRM).

Set up project

We login to our Nhost account and create a new project.

New Nhost project created

The Hasura instance and database comes ready with users table. We track all tables and relationships. Next we manually create two more tables for our CRM.

Table companies

The companies are each tenant in the CRM and each company are a customer to us.

Table for companies

Table customers

Each company can add and manage their customers in the CRM. All customers will be saved here in the customers table.

Adding the columns
Adding a Foreign Keys to the companies table

Next we'll add a foreign key to the users table. The customers.user_id is going to reference the user who is responsible for the customer. Not the we set the On Delete Violation to set null. So if the user thats responsible for the customer gets deleted, the value of customers.user_id will automatically be set to null.

Adding a foreign key to the users table

Last thing before adding this customers table is to set a unique constraint on the email column. This will make sure we won't have any duplicated customers with the same email.

Adding a unique constraint on the email

Table customer_comments

To at least add a little bit of functionality in our simplistic CRM we add the functionality to add and manage comments for every customer. So each company can make internal notes for each customer.

Updating the users table

Now we have our tables set up. Next we will add the column company_id to our users table so that every user is part of a company. We'll also create a foreign key between the users and the companies tables.

Adding company_id as a column on the users table
Adding the foreign key to the companies table

Insert test data

Our tables are setup correctly and it's time to set up some example data. First, we will add

Adding two test users

Let's add two test users for two different companies. We'll add the users in the Nhost console.

Adding Alice from Nike
Adding user Bob for company Coca-Cola

Next, let's add the two companies in the companies table in the database.

Add company Nike
Add company Coca-Cola
Our two companies in the database

Next, let's take the id of the respective companies and update the users company_id field.

Update company_id for Alice

Then we will activate the column company_id as a permission variable (session variable) for Hasura to be able to use the company_id in Hasuras permission system. We check the company_id in the Hasura console under Settings -> Authentication -> Edit.

Adding permission variable company_id

Next, let's track all relationships in Hasura. This enables Hasura to follow all foreign keys and create the relationships for the GraphQL API. Klick Track All next to Untracked foreign-key relations.

Track all relationships in Hasura

Hasura permissions

Time to create the permissions to make sure users are only allowed to insert/select/update/delete correctly. We'll start with the insert in the customers table.

Insert

When a user inserts a new customer we want that new customer to have the correct fields for user_id and company_id so a user from ex company Nike only can insert customer that's associated to company Nike. Also we want the customer to be connected to the user.

We do this by Column presets. Column presets will automatically populate a column in the database with a value from a session variable. This makes sure the value is correct and can not be tampered with by any users.

So we set user_id to get the value in the session variable x-hasura-user-id and company_id to get the value in the session variable x-hasura-company-id.

We also only enable a user to insert values for email and name so all other values gets set automatically.

Insert permission for a customer

Select

For select we only want users to be able to select customers that has the same company_id as the x-hasura-company-id session variable as the user. So we make the following check for selecting customers:

{
  "company_id": {
    "_eq": "X-Hasura-company-id"
  }
}

Test the permissions

Ok, so we have made our tables and we have created some permissions. Time to test our backend. Let's go back to the Hasura console and to the GraphQL API tab. Here we can select a specific user to make the request from. We select the user in the select input in the top left corner. We insert one customer with Alice at Nike and another user with Bob at Coca-Cola.

Insert a user as alice@nike.io
Insert a customer as bob@cocacola.com

We now have to customers in our database. One customer associated with Nike and another with Coca-Cola.

Two customers in the customer's database

Back in our GraphQL API, we make a request to get all customers as alice@nike.io. As you see, we only get the one customer associated with the company which Alice is part of.

Same thing if we switch user to bob@cocacola.com and make the exact same query.

Summary

In this blog post we have showed you how you can model your multi tenant application with Nhost and Hasura. This is done by using the session variables for a user and make the appropriate permission rules with custom permission checks and column presets.