Nestjs + Prisma + PostgreSQL〜RLS使用Nestjs-Prisma,Nestjs-CLS和Prisma客户端扩展
#node #postgres #prisma #nestjs

这是一种使用客户端扩展和异步alstorage(通过Nestjs-CLS)在Nestjs中进行多租户的简单方法。

本文描述了通用Nestjs实现的基础知识。查看this repo以获取一个完整且功能的示例应用程序。

步骤#1:使用bootstrap函数中使用nestjs-cls设置asynclocalstorage:

// main.ts
import {ClsMiddleware} from 'nestjs-cls';

async function bootstrap() {
  // init app...

  app.use(
    new ClsMiddleware({
      async setup(cls, req) {
        cls.set('TENANT_ID', req.params('tenant_id'));
      },
    }).use
  );

  // etc
}

步骤#2:将ClsModule.forRoot({ global:true })添加到您的应用模块(app.module.ts)导入。

步骤#3:创建一个导出自定义工厂提供商的文件,该文件返回扩展Prisma客户端。

// prisma-tenancy.provider.ts
import { PrismaModule, PrismaService } from 'nestjs-prisma';
import { ClsService } from 'nestjs-cls';

const useFactory = (prisma: PrismaService, store: ClsService) => {
    return prisma.$extends({
        query: {
            $allModels: {
                async $allOperations({ args, query }) {
                    const tenantId = store.get('TENANT_ID');


                    const [, result] = await prisma.$transaction([
                        prisma.$executeRaw`SELECT set_config('tenancy.tenant_id', ${`${tenantId || 0}`}, TRUE)`,
                        query(args),
                    ]);

                    return result;
                },
            },
        },
    });
};

export type ExtendedTenantClient = ReturnType<typeof useFactory>;

export const TENANCY_CLIENT_TOKEN = Symbol('TENANCY_CLIENT_TOKEN');

export const PrismaTenancyClientProvider = {
    provide: TENANCY_CLIENT_TOKEN,
    imports: [PrismaModule],
    inject: [PrismaService, ClsService],
    useFactory
};

步骤#4:创建一个导出上述工厂提供商的模块

// prisma-tenancy.module.ts
import { Module, Global } from '@nestjs/common';
import { PrismaTenancyClientProvider, TENANCY_CLIENT_TOKEN } from './prisma-tenancy.provider';
import { PrismaModule } from 'nestjs-prisma';

@Global()
@Module({
    imports: [PrismaModule],
    providers: [PrismaTenancyClientProvider],
    exports: [TENANCY_CLIENT_TOKEN]
})
export class PrismaTenancyModule { }

步骤#5:将上面的模块添加到root app.module.ts导入。

现在,您可以使用代币为您的自定义提供商注入扩展客户端(在这种情况下为TENANCY_CLIENT_TOKEN)。

import {Injectable, Inject} from "@nestjs/common";
import {
  TENANCY_CLIENT_TOKEN,
  ExtendedTenantClient,
} from "./prisma-tenancy.provider";

@Injectable()
export class SomeService {
  constructor(
    @Inject(TENANCY_CLIENT_TOKEN) private readonly prisma: ExtendedTenantClient
  ) {}

  // etc
}

有用的链接