Using Strapi as a multi-tenant

Hello to the Strapi team and the Community :wave:,

With our team, we’ve been taking a look at Strapi to compare with our current CMS and we were looking into using Strapi as a multi-tenant.

We provide a mobile application to multiple companies, each one of them has their own private data to administrate and store in the DB.

In Strapi we can create Roles for each of these companies and restrict the data they can access using RBAC, so no big problem on the administration side.

Now for limiting access to the data from the API it gets a bit more difficult.

Each company will have one and only one User/Token that will be used the access the Strapi back-end when making API calls.

By default calling the route of a content-type will return all the data of that content-type, meaning we will get the data of all companies on an API call, and of course we don’t want that.

We can filter the data with a policy, and by adding a tenant_id relational field linked to User to our content-types, we can now check that the id of the User making the api call is the same as the content-type's tenant_id to filter the data, meaning a tenant can now only call the content linked to it’s User's id, great!

The problem now is for adding the tenant_id when an Admin User creates content, we need the field to be autocompleted with the id of the User that will make the API request.

How can we make it so that when an Admin User of Role “Company X” add content, the tenant_id field will be autocompleted with the id of the User corresponding to “Company X” ?

1 Like

Hi @TristanTrvl great question. You can achieve this by overriding the default controller in the ./api/model_name/controllers/model_name.js file. The controller you’re looking for is called create. If you create a new async function called create inside the exported object, you have access to the ctx parameter on that function. This param contains information about the request that has been made to this controller. On this ctx object you will have a state parameter that among other things, will have a property called user set to the user that is making the request if they are authenticated while making the request. You can fetch the id on that user object and set it to the tenant_id field on your content type.

Here’s an example:
I have a model called Item and it has two fields, name and user. I populate the user field in the manner I’ve described above.

"use strict";
const { sanitizeEntity } = require("strapi-utils"); // this function removes all private fields from a model and it's relations

module.exports = {
  async create(ctx) {
    let entity = await strapi.services.item.create({
      ...ctx.request.body, // the user is always on the context state object when an authenticated request is made
      user: ctx.state.user,
    }); // strapi.services.item.create uses the auto-generated create service for this model to create the entity
    return sanitizeEntity(entity, { model: strapi.models.item }); // return the sanitized entity always
  },
};

1 Like

Hello @TristanTrvl ,

I have the same needs: share global models amongst many organisations able to settle and get their own data.
Did you succeed with Strapi to implement it ?
thx
Eric.