Setting Up A Multi-Tenant Application With Firebase, GraphQL, and Angular

Share on facebook
Share on google
Share on twitter
Share on linkedin

In this post, I will show you, how to set up a multi-tenant application with Firebase, GraphQL, and Angular. This is a common scenario for SaaS applications, as multiple tenants should be able to have their data separated from other tenants’ data and that data should be kept safe as well using proper authorization checks.

We will cover, how to design the Firebase Firestore database and setup Firebase Authentication to support multiple tenants as well as how to implement the necessary authorization checks on a GraphQL NodeJS server hosted with Firebase functions. There is going to be an Angular app, containing the course portal, which among others, are used for Angular Architect Accelerator.

Design the Firestore database

First, we are going to design our Firestore database to support multiple tenants. We are going to do this, by creating a schools collection, containing the different schools (tenants). The tenant’s data are then nested under these schools, that way the tenant’s data is split among separate vertical slices, which makes the security and tenant management easier.

Under each school (tenant), the tenant data is users (containing user-specific data), and course collection. Each course includes sections, lessons, and resources collections (containing the necessary data for each school).

On the top level, the schools collection contains the tenants, which contains courses and users:

 

 

Each course contains sections, resources, and lessons, where the sections contain a list of referenced lessons:

We store user data such as completed lessons and action items on each users’ entry:

 

 

 

Having that set up, our database now supports multiple-tenants, as well as user and course-specific data for each tenant.

Activating multi-tenant login

To support multi-tenant authentication we will use Firebase Authentication + Google Cloud Platforms’ Identity Platform, which will provide us with multi-tenant support.

You will need to go to Identity Platform in Google Cloud Platform and enable it. Here you can create tenants and add users to a specific tenant.

Integrating with the GraphQL server

We need to do these things in the GraphQL server:

  1. Verify id token on the requester and setting role and tenant id to each resolver, so we have it handy for all resolvers
  2. Use the tenants when reading and writing from/to Firestore

Verify id token and set context

For convenience, we want to get the schoolId from an HTTP header, we are setting in our GraphQL context, so we can access it in every query/mutation without the client needing to provide it explicitly in the GrahQL payload.
Also, we want to get the user id and validate, that the requesting user has a valid token. We are using Firebase Authentication for authentication on the Angular client.

We set this data, as well as verifies the id token in the Apollo server context middleware (on our GraphQL NodeJS server):

Use the context in the resolvers

In the queries and mutations, we want to check, that the uid in a payload, matches the current user’s userid (which we get from context) or the user is an admin of that user’s school.

Here we are using the context data to check if the requesting user is authorized to update the lesson’s completed status and is using the schoolId (tenantId) to update Firestore.

Setting up the clients for multi-tenancy

Since the clients need to tell the server on each request, which school it belongs to, it is convenient to do that using a header in an HTTP interceptor.

In this snippet, the schoolId and idToken is set as HTTP headers on each request and is used in the Apollo context as shown in the previous section.

Clients routing flow

For the routing, we need the route to start with the schoolId param, so we can set the school id.

The SchoolIdResolver is setting the Firebase Authentication tenant id:

After the user is logged in, it is navigated to a page for selecting the course:

Conclusion

What’s it! Now multiple tenants can use your application and have their data nicely separated and secured on your backend. Also, we saw how to make it simple for the clients to provide the tenant identifier (schoolId) to the server on each request, as well as having the routing set up for multi-tenancy.

Do you want to become an Angular architect? Check out Angular Architect Accelerator.

Related Posts and Comments

How to Set up a CI pipeline with Azure Pipelines and Nx

It goes without saying that having a CI pipeline for your Angular apps is a must. Setting one up for regular Angular apps is fairly straightforward but when you have an Nx monorepo there are certain other challenges that you have to overcome to successfully orchestrate a “build once, deploy many” pipeline. This post will

Read More »

How to Set Up Git Hooks in an Nx Repo

Git hooks can be used to automate tasks in your development workflow. The earlier a bug is discovered, the cheaper it is to fix (and the less impact it has). Therefore it can be helpful to run tasks such as linting, formatting, and tests when you are e.g. committing and pushing your code, so any

Read More »

The Stages of an Angular Architecture with Nx

Long gone are the times when the frontend was just a dumb static website. Frontend apps have gotten increasingly complex since the rise of single-page application frameworks like Angular. It comes with the price of increased complexity and the ever-changing frontend landscape requires you to have an architecture that allows you to scale and adapt

Read More »

The Best Way to Use Signals in Angular Apps

Since Angular 16, Angular now has experimental support for signals and there is a lot of confusion in the community about whether this is going to replace RxJS or how it should be used in an app in combination with RxJS. This blog post sheds some light on what I think is the best way

Read More »

High ROI Testing with Cypress Component Testing

Testing is one of the most struggled topics in Angular development and many developers are either giving up testing altogether or applying inefficient testing practices consuming all their precious time while giving few results in return. This blog post will change all this as we will cover how I overcame these struggles the hard way

Read More »