Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change tenant on the fly #11

Open
samuelpares opened this issue Dec 13, 2021 · 5 comments
Open

Change tenant on the fly #11

samuelpares opened this issue Dec 13, 2021 · 5 comments
Labels
help wanted Extra attention is needed question Further information is requested

Comments

@samuelpares
Copy link

How can I change the tenant on the fly?

Use case:
My tenant can have multiple users.
The users will be scoped in the tenant database.
So, when I'm creating a new Tenant in the default database, there will be in the payload as well the first user (tenant owner).
So, right after creating the tenant in the default database, I want to change to the tenant database to insert the user.
Is there a way to do this in the same request for the tenant creation or do I need to to in another request, this time passing the tenant to the request?

@sandeepsuvit
Copy link
Contributor

@samuelpares thanks for writing in. I am guessing the current way this library is developed is by handling the switching per request since the toggling of tenants depends on the identifier in the header. But if you need to switch to a tenant database right after creation that has to be custom handled at your service level by opening a connection to the tenant db or like you mentioned by making another request with the tenant identifier attached.

Do you see a better solution to this? Happy to hear your opinion aswell :)

@Daniel-Boll
Copy link

I think a good solution would be in a scenario where I can within a request change the current tenant, think of.

@Get(':tenantId')
async changeTenantOnRequest(@Param('tenantId') tenantId: string) {
	changeTenantHere(tenantId);
	// Now models are connected to this model
}

The only way of achieving this is through a manual connection to mongoose?

@samuelpares
Copy link
Author

@sandeepsuvit, first of all, thanks for this nice lib, it's helping me a lot writing multitenant apps.

As for my opnion, since I'm very new to nestjs, I can't say if it is possible, but a good solution would be to have a method I could call passing the db name to change, as Daniel said above. This would solve my use case, and another one I faced now: allow me to use it in a Bull Queue processor.. today I'm importing mongoose again and doing everything manually.

@sandeepsuvit
Copy link
Contributor

@samuelpares thanks for your kind words :)

The way this library is architected is by switching the db context at two levels

  • Subdomain prefix as a tenant identifier
  • Request param tenantIdentifier: X-TENANT-ID as a tenant identifier

I assume making another call to switch the db post that (in an HTTP request response cycle) may not be required since the tenant connection would have been resolved by then and made available to you as we can see here

private static getTenant(
        req: Request,
        moduleOptions: TenancyModuleOptions,
        adapterHost: HttpAdapterHost,
    ): string {
        // Check if the adaptor is fastify
        const isFastifyAdaptor = this.adapterIsFastify(adapterHost);

        if (!moduleOptions) {
            throw new BadRequestException(`Tenant options are mandatory`);
        }

        // Extract the tenant identifier
        const {
            tenantIdentifier = null,
            isTenantFromSubdomain = false,
        } = moduleOptions;

        // Pull the tenant id from the subdomain
        if (isTenantFromSubdomain) {

            return this.getTenantFromSubdomain(isFastifyAdaptor, req);

        } else {
            // Validate if tenant identifier token is present
            if (!tenantIdentifier) {
                throw new BadRequestException(`${tenantIdentifier} is mandatory`);
            }

            return this.getTenantFromRequest(isFastifyAdaptor, req, tenantIdentifier);
        }
    }

But as per your usecase switching the db context in a Queue processor is something the library doesn't support simply because we don't have access to the request scope there since the queue process is normally an asynchronous operation that doesn't fall under the http request response lifecycle.

My one suggestion is to have a common service TenantConnectionService created on your side to manage this manual switching which can be reused in other so called queue operations.

If you have any other suggestions other than what i proposed above i would be happy to hear that.

@samuelpares
Copy link
Author

samuelpares commented Jan 23, 2022

It sounds like good approach, but I don't have the expertise to implement it. So I added to my job data the database name. In the processor function I get the database name and create a new mongoose connection. Although it's not the best solution, it works fine.

Another situation I have now: cron jobs!
I'm creating dynamic jobs using the SchedulerRegistry.addCronJob(name, job).
So in my service I created the function:

  addCronJob(name: string, dateTime: Date) {
    const job = new CronJob(dateTime, async () => {
      console.log(`time (${dateTime}) for job ${name} to run!`);
      const a = await this.findOne('61ed5f605c128a50a8991809');
      console.log(a);
    });

    this.schedulerRegistry.addCronJob(name, job);
    job.start();

    console.log('job added');
  }

The await this.findOne() is using the model injected with tenancy..
Creating the job to be executed within 10 seconds, worked fine, in the right connection.
I imagine that if schedule it to a week for now, it would not work, right?

@sandeepsuvit sandeepsuvit added help wanted Extra attention is needed question Further information is requested labels Jun 23, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed question Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants