Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to cancel mongoose find execution and return data from redis instead?

I’m trying to implement Redis cache along with mongoose in nest.js and I'm looking for a way to check redis cache first before executing find or findOne and return the data from redis otherwise execute query, save result in redis and return the result. The reason I'm not implementing caching as recomended by nest.js is that I'm also using Apollo Server for GraphQL.

@Injectable()
export class MyService {
    async getItem(where): Promise<ItemModel> {
        const fromCache = await this.cacheService.getValue('itemId');
        if(!!fromCache){
            return JSON.parse(fromCache);
        } else {
            const response = await this.ItemModel.find(where);
            this.cacheService.setValue('itemId', JSON.stringify(response));
            return response
        }
    }
}

I'd like to move this piece of code to a single place so that I don't have to repeat this code for each query in my code since I have multiple services. I know mongoose middleware has a way to run pre and post functions on queries but I'm just not sure how to accomplish this using.

These are the versions I'm using:

  • nestjs v7
  • mongoose v5.10.0
like image 366
José Galván Avatar asked Nov 06 '22 04:11

José Galván


1 Answers

You could create a method decorator where you move the logic to:

export const UseCache = (cacheKey:string) => (_target: any, _field: string, descriptor: TypedPropertyDescriptor<any>) => {
    const originalMethod = descriptor.value;
    // note: important to use non-arrow function here to preserve this-context
    descriptor.value     = async function(...args: any[]) {
        const fromCache = await this.cacheService.getValue(cacheKey);
        if(!!fromCache){
            return JSON.parse(fromCache);
        }
        const result = await originalMethod.apply(this, args);
        await this.cacheService.setValue(cacheKey, JSON.stringify(result));
        return result;
    };
}

Then use it with:

@Injectable()
export class MyService {   

    constructor(private readonly cacheService:CacheService) { .. }

    @UseCache('itemId')
    async getItem(where): Promise<ItemModel> {        
        return this.ItemModel.find(where);
    }

    @UseCache('anotherCacheKey')
    async anotherMethodWithCache(): Promise<any> {        
         // ...            
    }
}
like image 134
eol Avatar answered Nov 12 '22 16:11

eol