Created
June 28, 2024 10:09
-
-
Save adevinwild/b526fd8f12c9d93b3999547e4a1aee8c to your computer and use it in GitHub Desktop.
An implementation of the RedisCacheService on the ProductService for Medusa 1.20.x
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type RedisCacheService from '@medusajs/cache-redis/dist/services/redis-cache'; | |
import { ProductService as MedusaProductService, Product } from '@medusajs/medusa'; | |
import { CreateProductInput, FindProductConfig, ProductSelector, UpdateProductInput } from '@medusajs/medusa/dist/types/product'; | |
import type { Redis } from 'ioredis'; | |
type InjectedDependencies = { | |
cacheService: RedisCacheService | |
redisClient: Redis | |
} | |
class ProductService extends MedusaProductService { | |
protected readonly cacheService_: RedisCacheService | |
protected readonly redisClient_: Redis | |
static cacheKeyPrefix = 'products:listAndCount'; | |
constructor(container: InjectedDependencies) { | |
// @ts-ignore | |
super(...arguments); | |
this.cacheService_ = container.cacheService; | |
this.redisClient_ = container.redisClient; | |
} | |
async listAndCount(selector: ProductSelector, config?: FindProductConfig): Promise<[Product[], number]> { | |
const cacheKey = this.generateCacheKey(selector, config); // Just to be sure that we have the same keys in the same order | |
const ttl = 60 // 1 minute, TTL is in seconds (adjust as needed) | |
// Check if the cache is expired | |
const cached = await this.cacheService_.get(cacheKey) as { products: Product[], count: number }; | |
if (cached) { | |
console.log('cached data found'); | |
// Return the cached data if it's not expired | |
return [cached.products, cached.count]; | |
} | |
const [products, count] = await super.listAndCount(selector, config); | |
// Otherwise, set the cache with the new data | |
await this.cacheService_.set(cacheKey, { products, count }, ttl); | |
return [products, count]; | |
} | |
async create(productObject: CreateProductInput): Promise<Product> { | |
const product = await super.create(productObject); | |
// Whenever a product is created, we want to invalidate the cache | |
await this.cacheService_.invalidate(`${ProductService.cacheKeyPrefix}:*`); | |
return product; | |
} | |
async update(productId: string, update: UpdateProductInput): Promise<Product> { | |
const product = await super.update(productId, update); | |
// Whenever a product is updated, we want to invalidate the cache | |
await this.cacheService_.invalidate(`${ProductService.cacheKeyPrefix}:*`); | |
return product; | |
} | |
private generateCacheKey(selector: ProductSelector, config?: FindProductConfig): string { | |
const sortedSelector = this.sortObject(selector); | |
const sortedConfig = config ? this.sortObject(config) : undefined; | |
// No matter how the objects are sorted, we want to keep the same keys in the same order | |
return `${ProductService.cacheKeyPrefix}:${JSON.stringify(sortedSelector)}:${JSON.stringify(sortedConfig)}`; | |
} | |
private sortObject<T>(obj: T): T { | |
if (Array.isArray(obj)) { | |
return obj.map(item => this.sortObject(item)).sort() as any; | |
} else if (typeof obj === 'object' && obj !== null) { | |
return Object.keys(obj).sort().reduce((result, key) => { | |
result[key as keyof T] = this.sortObject(obj[key as keyof T]); | |
return result; | |
}, {} as T); | |
} | |
return obj; | |
} | |
} | |
export default ProductService |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment