How to inject Cache Manager in My Custom Provider - node.js

I try to use cache-manager in my nestjs web app.
In my Module I register my Cache
#Module({
imports: [
_CacheModule.register(),
],
})
export class CacheModule {}
From the same mudule I have another custom provider which need to inject CacheManager.
import {Cache} from '#nestjs/common'
...
async function providerFactory(
config: Config,
cache: Cache,
logger: Logger,
): Promise<Store> {
return process.env.NODE_ENV === 'development'
? new StoreA(keyValueConfig, cache, logger)
: new StoreB(keyValueConfig, logger);
}
export const RedisOrMockKeyValueStoreProvider: Provider = {
provide: Store,
useFactory: providerFactory,
inject: [Config, Cache, Logger],
};
But I got Cache is not defined Error when the app start. How should I inject the cache into my custom provider?

You should use the CACHE_MANAGER injection token instead of Cache

Related

How to use NestJS Versioning with Router module

In our nest 8 project we use the RouterModule for routing between different parts of the app:
#Module({
imports: [
RouterModule.register([
{
path: '/internal',
module: InternalModule
},
{
path: '/external',
module: ExternalModule
}
]),
],
})
export class AppModule {}
In the InternalModule we would like to use NestJS versioning:
#Controller('someroute')
export class InternalController {
#Get(':id')
#Version(['1'])
async getPerson(#Param('id') id): Promise<any> {}
}
Routing without versioning works, versioning without routing too, but the combination does not.
I tried calling it via "localhost:port/v1/internal/someroute" and "localhost:port/internal/v1/someroute"
Versioning is enabled:
app.enableVersioning({
type: VersioningType.URI
});

Nestjs - connect bull-board in a normal controller

I created an app using nest.js and bull.
I added bull-board package to monitor my queues, but in documentation, only one way to add it to the app is mount as middleware:
In main.ts:
app.use('/admin/queues', bullUI);
Is there any way to add bullUI in a normal nest controller, after jwt auth? Like:
#UseGuards(JwtAuthGuard)
#Get("queues")
activate() {
return UI
}
You can use any express middleware like this inside controllers, but maybe some cases cause errors like serving static files with Guard exception and etc.
#UseGuards(JwtAuthGuard)
#Get("queues/*")
activate(#Req() req, #Res() res) {
bullUI(req, res)
}
I've got this working via a middleware consumer, so something like this:
import { router } from 'bull-board';
#Module({
imports: [
NestBullModule.forRoot({ redis }),
],
providers: [],
})
export class BullModule {
configure(consumer: MiddlewareConsumer): void {
consumer
.apply(router)
.forRoutes('/admin/queues');
}
}
I'd like to extend the original answer of #JCF, mainly, because it's working and much easier to understand.
I am using not default bull, with #nestjs/queue, but an improved version of BullMQ from anchan828 repo, with NestJS decorators, but I guess in both cases, the result will be the same.
The queue.module file:
#Module({
imports: [
BullModule.forRoot({
options: {
connection: {
host: redisConfig.host,
port: redisConfig.port,
},
},
}),
/** DI all your queues and Redis connection */
BullModule.registerQueue('yourQueueName'),
],
controllers: [],
providers: [],
})
export class QueueModule {
constructor (
#BullQueueInject('yourQueueName')
private readonly queueOne: Queue,
) {
/** Add queues with adapter, one-by-one */
setQueues([new BullMQAdapter(this.queueOne, { readOnlyMode: false })])
}
configure(consumer: MiddlewareConsumer): void {
consumer
.apply(router)
.forRoutes('/admin/queues');
}
}
Then just add it, to parent AppModule, via import, like that:
I am not sure, that Redis connection is needed here, for parent AppModule
#Module({
imports: [
BullModule.forRoot({
options: {
connection: {
host: redisConfig.host,
port: redisConfig.port,
},
},
}),
QueueModule
],
controllers: [],
providers: [],
})
export class AppModule {}
run the main.js, and visit, localhost:8000/admin/queues

Caching return value from a service method

I am using nestjs and have just installed the cache-manager module and are trying to cache a response from a service call.
I register the cache module in a sample module (sample.module.ts):
import { CacheInterceptor, CacheModule, Module } from '#nestjs/common';
import { SampleService } from './sample.service';
import { APP_INTERCEPTOR } from '#nestjs/core';
import * as redisStore from 'cache-manager-redis-store';
#Module({
imports: [
CacheModule.register({
ttl: 10,
store: redisStore,
host: 'localhost',
port: 6379,
}),
],
providers: [
SampleService,
{
provide: APP_INTERCEPTOR,
useClass: CacheInterceptor,
}
],
exports: [SampleService],
})
export class SampleModule {}
Then in my service (sample.service.ts):
#Injectable()
export class SampleService {
#UseInterceptors(CacheInterceptor)
#CacheKey('findAll')
async findAll() {
// Make external API call
}
}
Looking at redis I can see that nothing is cached for the service method call. If I use the same approach with a controller, then everything works fine and I can see the cached entry in my redis database. I am thinking that there is no way out of the box to cache individual service method calls in nestjs.
Reading the documentation it seems that I am only able to use this approach for controllers, microservices and websockets, but not ordinary services?
Correct, it is not possible to use the cache the same way for services as for controllers.
This is because the magic happens in the CacheInterceptor and Interceptors can only be used in Controllers.
However, you can inject the cacheManager into your service and use it directly:
export class SampleService {
constructor(#Inject(CACHE_MANAGER) protected readonly cacheManager) {}
findAll() {
const value = await this.cacheManager.get(key)
if (value) {
return value
}
const respone = // ...
this.cacheManager.set(key, response, ttl)
return response
}

Dotenv variable is undefined

I have one module (auth module) where I register JwtModule:
JwtModule.register({
secret: process.env.SECRET_KEY,
signOptions: { expiresIn: '60m' },
}),
Register function looks like this: static register(options: JwtModuleOptions): DynamicModule;
JwtModule is defined in node_modules (jwt.module.js)
let JwtModule = JwtModule_1 = class JwtModule {
static register(options) {
console.log(options);
console.log(process.env.SECRET_KEY);
return {
module: JwtModule_1,
providers: jwt_providers_1.createJwtProvider(options)
};
}
In app.module.ts I have
ConfigModule.forRoot({
isGlobal: true,
}),
process.env.SECRET_KEY is undefined in node_modules file (console.log(process.env.SECRET_KEY)), but signOptions: { expiresIn: '60m' } is defined.
If I try to write to console SECRET_KEY from .env everywhere else is defined, but in node_modules is undefined.
The ConfigModule's ConfigService runs dotenv's config() method, but all decorators run at the same time, so the config() method is not called yet. You can use the async registration instead and use the ConfigService like so
JwtModule.registerAsync({
inject: [ConfigService],
useFactory: (config: ConfigService) => ({
secret: config.get('SECRET_KEY')
})
})
Be sure that you Call the ConfigService in your App Module constructor :
export class AppModule {
constructor(private configService: ConfigService) {}
}

NestJS, using typeorm on providers

I want to make a provider which queries an entity from db. (what interests me actually is to make typeorm available when I create a provider).
I get
[ExceptionHandler] Connection "default" was not found. - {"trace":"ConnectionNotFoundError: Connection \"default\" was not found.\n
I tried using service, using custom repository, nothing works. I have something like this in module:
{
provide: MICROSERVICE_ID,
useFactory: async (): Promise<Microservice> => {
//ignore logic
return await getRepository(Microservice).findOne(1);
},
inject: []
}
TypeOrm is imported on app.module.ts
TypeOrmModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: async (configService: ConfigService) => (configService.get('database'))
}),
Any help is appreciated.
Check your database file. The Entity may not load as expected.
For newest NestJS version. We should load from ./dist/
Fixed by injecting Connection from typeorm.
import { getRepository, Connection } from 'typeorm';
{
provide: MICROSERVICE_ID,
useFactory: async (): Promise<Microservice> => {
//ignore logic
return await getRepository(Microservice).findOne(1);
},
inject: [Connection]
}

Resources