Skip to content

Instantly share code, notes, and snippets.

@rajeshpillai
Last active March 18, 2025 10:39
Show Gist options
  • Select an option

  • Save rajeshpillai/b3cc78ffabca9be31b5ce0a9bc01e7df to your computer and use it in GitHub Desktop.

Select an option

Save rajeshpillai/b3cc78ffabca9be31b5ce0a9bc01e7df to your computer and use it in GitHub Desktop.

Revisions

  1. rajeshpillai revised this gist Mar 18, 2025. 2 changed files with 109 additions and 18 deletions.
    18 changes: 0 additions & 18 deletions base_cache.js
    Original file line number Diff line number Diff line change
    @@ -1,18 +0,0 @@
    // cache/baseCache.js
    // This is our abstract base class defining the contract for our cache implementations.
    class BaseCache {
    async get(key) {
    throw new Error('Method "get" must be implemented');
    }

    async set(key, value, ttl) {
    throw new Error('Method "set" must be implemented');
    }

    async del(key) {
    throw new Error('Method "del" must be implemented');
    }
    }

    module.exports = BaseCache;

    109 changes: 109 additions & 0 deletions cache.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,109 @@
    // cache/baseCache.js
    // This is our abstract base class defining the contract for our cache implementations.
    class BaseCache {
    async get(key) {
    throw new Error('Method "get" must be implemented');
    }

    async set(key, value, ttl) {
    throw new Error('Method "set" must be implemented');
    }

    async del(key) {
    throw new Error('Method "del" must be implemented');
    }
    }

    module.exports = BaseCache;

    // cache/in_memory_cache.js
    require('dotenv').config();
    const BaseCache = require('./baseCache');

    const DEFAULT_TTL = process.env.CACHE_DEFAULT_TTL
    ? parseInt(process.env.CACHE_DEFAULT_TTL, 10)
    : 60;

    class InMemoryCache extends BaseCache {
    constructor(defaultTTL = DEFAULT_TTL) {
    super();
    this.store = new Map();
    this.defaultTTL = defaultTTL;
    }

    async get(key) {
    const record = this.store.get(key);
    if (!record) return null;
    // Check if expired
    if (record.expireAt && record.expireAt < Date.now()) {
    this.store.delete(key);
    return null;
    }
    return record.value;
    }

    async set(key, value, ttl) {
    ttl = ttl || this.defaultTTL;
    const expireAt = ttl ? Date.now() + ttl * 1000 : null;
    this.store.set(key, { value, expireAt });
    }

    async del(key) {
    return this.store.delete(key);
    }
    }

    module.exports = InMemoryCache;

    // cache/redis_cache.js
    require('dotenv').config();
    const Redis = require('ioredis');
    const BaseCache = require('./baseCache');

    const DEFAULT_TTL = process.env.CACHE_DEFAULT_TTL
    ? parseInt(process.env.CACHE_DEFAULT_TTL, 10)
    : 60;

    class RedisCache extends BaseCache {
    constructor(defaultTTL = DEFAULT_TTL) {
    super();
    this.defaultTTL = defaultTTL;
    this.client = new Redis(process.env.REDIS_URL);
    this.client.on('error', (err) => {
    console.error('Redis error:', err);
    });
    }

    async get(key) {
    return await this.client.get(key);
    }

    async set(key, value, ttl) {
    ttl = ttl || this.defaultTTL;
    if (ttl) {
    await this.client.set(key, value, 'EX', ttl);
    } else {
    await this.client.set(key, value);
    }
    }

    async del(key) {
    await this.client.del(key);
    }
    }

    module.exports = RedisCache;

    // cache/index.js
    require('dotenv').config();
    const InMemoryCache = require('./inMemoryCache');
    const RedisCache = require('./redisCache');

    // Choose Redis in production; otherwise use in-memory cache.
    const cache = process.env.NODE_ENV === 'production'
    ? new RedisCache()
    : new InMemoryCache();

    module.exports = cache;


  2. rajeshpillai revised this gist Mar 18, 2025. 2 changed files with 18 additions and 71 deletions.
    18 changes: 18 additions & 0 deletions base_cache.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,18 @@
    // cache/baseCache.js
    // This is our abstract base class defining the contract for our cache implementations.
    class BaseCache {
    async get(key) {
    throw new Error('Method "get" must be implemented');
    }

    async set(key, value, ttl) {
    throw new Error('Method "set" must be implemented');
    }

    async del(key) {
    throw new Error('Method "del" must be implemented');
    }
    }

    module.exports = BaseCache;

    71 changes: 0 additions & 71 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -1,71 +0,0 @@
    require('dotenv').config(); // Ensure env variables are loaded
    const Redis = require('ioredis');

    // Use an environment variable or default to 60 seconds
    const DEFAULT_TTL = process.env.CACHE_DEFAULT_TTL ? parseInt(process.env.CACHE_DEFAULT_TTL, 10) : 60;

    class InMemoryCache {
    constructor(defaultTTL = DEFAULT_TTL) {
    this.store = new Map();
    this.defaultTTL = defaultTTL;
    }

    async get(key) {
    const record = this.store.get(key);
    if (!record) return null;
    // Check if the key has expired
    if (record.expireAt && record.expireAt < Date.now()) {
    this.store.delete(key);
    return null;
    }
    return record.value;
    }

    async set(key, value, ttlInSeconds) {
    // Use provided TTL or fallback to the default TTL
    ttlInSeconds = ttlInSeconds || this.defaultTTL;
    const expireAt = ttlInSeconds ? Date.now() + ttlInSeconds * 1000 : null;
    this.store.set(key, { value, expireAt });
    }

    async del(key) {
    return this.store.delete(key);
    }
    }

    class RedisCache {
    constructor(defaultTTL = DEFAULT_TTL) {
    this.defaultTTL = defaultTTL;
    // Create a new ioredis client using the connection string from environment variables
    this.client = new Redis(process.env.REDIS_URL);
    this.client.on('error', (err) => {
    console.error('Redis error:', err);
    });
    }

    async get(key) {
    return await this.client.get(key);
    }

    async set(key, value, ttlInSeconds) {
    // Use provided TTL or fallback to the default TTL
    ttlInSeconds = ttlInSeconds || this.defaultTTL;
    if (ttlInSeconds) {
    // Use the EX option to set an expiration (in seconds)
    await this.client.set(key, value, 'EX', ttlInSeconds);
    } else {
    await this.client.set(key, value);
    }
    }

    async del(key) {
    await this.client.del(key);
    }
    }

    // Export the appropriate cache based on the environment.
    // In production mode use Redis; in all other cases (dev, testing) use in-memory cache.
    const cache =
    process.env.NODE_ENV === 'production' ? new RedisCache() : new InMemoryCache();

    module.exports = cache;
  3. rajeshpillai created this gist Mar 18, 2025.
    71 changes: 71 additions & 0 deletions gistfile1.txt
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,71 @@
    require('dotenv').config(); // Ensure env variables are loaded
    const Redis = require('ioredis');

    // Use an environment variable or default to 60 seconds
    const DEFAULT_TTL = process.env.CACHE_DEFAULT_TTL ? parseInt(process.env.CACHE_DEFAULT_TTL, 10) : 60;

    class InMemoryCache {
    constructor(defaultTTL = DEFAULT_TTL) {
    this.store = new Map();
    this.defaultTTL = defaultTTL;
    }

    async get(key) {
    const record = this.store.get(key);
    if (!record) return null;
    // Check if the key has expired
    if (record.expireAt && record.expireAt < Date.now()) {
    this.store.delete(key);
    return null;
    }
    return record.value;
    }

    async set(key, value, ttlInSeconds) {
    // Use provided TTL or fallback to the default TTL
    ttlInSeconds = ttlInSeconds || this.defaultTTL;
    const expireAt = ttlInSeconds ? Date.now() + ttlInSeconds * 1000 : null;
    this.store.set(key, { value, expireAt });
    }

    async del(key) {
    return this.store.delete(key);
    }
    }

    class RedisCache {
    constructor(defaultTTL = DEFAULT_TTL) {
    this.defaultTTL = defaultTTL;
    // Create a new ioredis client using the connection string from environment variables
    this.client = new Redis(process.env.REDIS_URL);
    this.client.on('error', (err) => {
    console.error('Redis error:', err);
    });
    }

    async get(key) {
    return await this.client.get(key);
    }

    async set(key, value, ttlInSeconds) {
    // Use provided TTL or fallback to the default TTL
    ttlInSeconds = ttlInSeconds || this.defaultTTL;
    if (ttlInSeconds) {
    // Use the EX option to set an expiration (in seconds)
    await this.client.set(key, value, 'EX', ttlInSeconds);
    } else {
    await this.client.set(key, value);
    }
    }

    async del(key) {
    await this.client.del(key);
    }
    }

    // Export the appropriate cache based on the environment.
    // In production mode use Redis; in all other cases (dev, testing) use in-memory cache.
    const cache =
    process.env.NODE_ENV === 'production' ? new RedisCache() : new InMemoryCache();

    module.exports = cache;