#Set provided value from external module

5 messages · Page 1 of 1 (latest)

graceful egret
#

Hi,

our API is built in such a way that all requests require to have a X-Tenant-Id header set, so we know how to instantiate repositories. All our repositories are set up in such a way:

@Injectable({ scope: Scope.REQUEST })
export class BaseRepository<T extends BaseTable> {
    protected tableClass: { new (...args: any[]): T };
    protected _repository: Repository<T>;

    public constructor(
        @Inject(DATA_SOURCE)
        protected readonly dataSource: DataSource,
        @Inject(TENANT_ID)
        public readonly tenantId: string,
    ) {}

    public get repository() {
        if (!this._repository) {
            this._repository = this.dataSource.getRepository(this.tableClass);
        }

        return this._repository;
    }

    public async findById(id: string) {
        return this.repository.findOne({
            where: {
                id,
                tenantId: this.tenantId,
            },
        });
    }
}

We then have a provider that provides this TENANT_ID value using the following logic:

const tenantIdProvider = {
    provide: TENANT_ID,
    scope: Scope.REQUEST,
    durable: true,
    useFactory: (request: Request | { tenantId: string }) => {
        return 'tenantId' in request
            ? request.tenantId
            : (request.headers['x-tenant-id'] as string);
    },
    inject: [REQUEST],
};

This works great with HTTP methods, because we provide is a header.

Now we want to implement a module that listens to Redis events (example, or some other external source), and we want to provide the tenantId via other means, such as via the payload of a message.

How do we properly initialise whatever module we're using to provide this tenant ID via other means?

It feels like we need to be able to (optionally) inject another type of context, instead of the HTTP context, but I wouldn't know where to start.

chilly lotus
graceful egret
#

I'm not sure if thats what I want. We're talking about a durable provider thats injected, and needs to stay that way within the context of that dependency tree. This seems like a bad solution, because it still doesn't allow me to set the same state from multiple entrypoints

glossy imp
#

You coupled the database layer to the request-scope. Request scope bubbless up to the top-most parent. Not all providers support request scope (for example websocket gateways, cron controllers), so you're not going to be able to use your repositories in an easy way.

What can be done is use the request context factory with a fake request (in case there is none) to build a request-scoped sub-tree and retrieve the request-scoped dependency via moduleRef.resolve. You won't be able to inject it normally through the constructor in a static (non request scoped) provider

#

I'd suggest you look into AsyncLocalStorage again, because it solves your exact use-case. And it absolutely does allow you to set the same state from multiple entry points.