npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

type-cacheable-dynamodb

v1.0.2

Published

TypeScript-based caching decorators

Downloads

27

Readme

Build Status

type-cacheable

TypeScript-based caching decorator to assist with caching (and clearing cache for) async methods. Currently supports Redis (redis, ioredis) and node-cache.

Usage

Installation

npm install --save type-cacheable

or

yarn add type-cacheable

Setup Adapter

You will need to set up the appropriate adapter for your cache. So far, there is only support for Redis (redis, ioredis) and node-cache. If you would like to see more adapters added, please open an issue or, better yet, a pull request with an implementation.

To use the Redis adapter, add the following code to your entry point:

import * as Redis from 'redis';
import { useRedisAdapter } from 'type-cacheable';

const client = Redis.createClient();
useRedisAdapter(client);

To use the ioredis adapter, add the following code to your entry point:

import * as IoRedis from 'ioredis';
import { useIoRedisAdapter } from 'type-cacheable';

const client = new IoRedis();
useIoRedisAdapter(client);

To use the node-cache adapter, add the following code to your entry point:

import * as NodeCache from 'node-cache';
import { useNodeCacheAdapter } from 'type-cacheable';

const client = new NodeCache.default();
useNodeCacheAdapter(client);

Change Global Options

Some options can be configured globally for all decorated methods. Here is an example of how you can change these options:

// Import and set adapter as above
client.setOptions(<CacheManagerOptions>{
  excludeContext: false, // Defaults to true. If you don't pass a specific hashKey into the decorators, one will be generated by serializing the arguments passed in and optionally the context of the instance the method is being called on.
  ttlSeconds: 0, // A global setting for the number of seconds the decorated method's results will be cached for.
});

Currently, there are two decorators available in this library: @Cacheable and @CacheClear. Here is a sample of how they can be used:

import * as Redis from 'redis';
import { Cacheable, CacheClear } from 'type-cacheable';

const userClient = Redis.createClient();

class TestClass {
  public aProp: string = 'aVal!';

  private userRepository: Repository<User>;

  // This static method is being called to generate a cache key based on the given arguments.
  // Not featured here: the second argument, context, which is the instance the method
  // was called on.
  static setCacheKey = (args: any[]) => args[0];

  // If getUserById('123') were called, the return value would be cached
  // in a hash under user:123, which would expire in 86400 seconds
  @Cacheable({ cacheKey: TestClass.setCacheKey, hashKey: 'user', client: userClient, ttlSeconds: 86400 })
  public async getUserById(id: string): Promise<any> {
    return this.userRepository.findOne(id);
  }

  // If getProp('123') were called, the return value would be cached
  // under 123 in this case for 10 seconds
  @Cacheable({ cacheKey: TestClass.setCacheKey, ttlSeconds: args => args[1] })
  public async getProp(id: string, cacheForSeconds: number): Promise<any> {
    return this.aProp;
  }

  // If setProp('123', 'newVal') were called, the value cached under
  // key 123 would be deleted in this case.
  @CacheClear({ cacheKey: TestClass.setCacheKey })
  public async setProp(id: string, value: string): Promise<void> {
    this.aProp = value;
  }
}

@Cacheable

The @Cacheable decorator first checks for the given key(s) in cache. If a value is available (and not expired), it will be returned. If no value is available, the decorated method will run, and the cache will be set with the return value of that method. It takes CacheOptions for an argument. The available options are:

interface CacheOptions {
  cacheKey?: string | CacheKeyBuilder; // Individual key the result of the decorated method should be stored on
  hashKey?: string | CacheKeyBuilder; // Set name the result of the decorated method should be stored on (for hashes)
  client?: CacheClient; // If you would prefer use a different cache client than passed into the adapter, set that here
  noop?: boolean; // Allows for consuming libraries to conditionally disable caching. Set this to true to disable caching for some reason.
  ttlSeconds?: number | TTLBuilder; // Number of seconds the cached key should live for
}

@CacheClear

The @CacheClear decorator first runs the decorated method. If that method does not throw, @CacheClear will delete the given key(s) in the cache. It takes CacheClearOptions for an argument. The available options are:

interface CacheClearOptions {
  cacheKey?: string | CacheKeyBuilder; // Individual key the result of the decorated method should be stored on
  hashKey?: string | CacheKeyBuilder; // Set name the result of the decorated method should be stored on (for hashes)
  client?: CacheClient; // If you would prefer use a different cache client than passed into the adapter, set that here
  noop?: boolean; // Allows for consuming libraries to conditionally disable caching. Set this to true to disable caching for some reason.
  isPattern?: boolean; // Will remove pattern matched keys from cache (ie: a 'foo' cacheKey will remove ['foolish', 'foo-bar'] matched keys assuming they exist)
}
CacheKeyBuilder

CacheKeyBuilder can be passed in as the value for cacheKey or hashKey on either @Cacheable or @CacheClear. This is a function that is passed two arguments, args and context, where args is the arguments the decorated method was called with, and context is the object (this value) the method was called on. This function must return a string.

For example, if you would like to cache a user, you might want to cache them by id. Refer to the sample above to see how this could be done.

Note

If no cacheKey is passed in, one will be generated by serializing and hashing the method name, arguments, and context in which the method was called. This will not allow you to reliably clear caches, but is available as a convenience.

Using Adapter directly

It can happen that you need to read/write data from cache directly, without decorators. To achieve this you can use cacheManager. For example:

import cacheManager from "type-cacheable";
import keyGenerator from "./utils/cacheKeyGenerator";

class UserService {

    private userRepository: Repository<User>;
    private rolesRepository: Repository<Role>;

    public async blockUser(id: string): Promise<void> {
        await this.userRepository.update({ id }, { isBlocked: true });
        const key = keyGenerator([id], CacheKey.UserRoles);
        await cacheManager.client?.del(key);
    }

    public async getUserDetails(id: string): Promise<UserWithRoles> {
        const key = keyGenerator([id], CacheKey.UserRoles);

        let userRoles = await cacheManager.client?.get(key);
        if (!userRoles) {
            userRoles = await this.rolesRepository.find({ userId: id });
            await cacheManager.client?.set(key, userRoles, 3600);
        }

        const user = await this.userRepository.findOne(id);

        return { ...user, userRoles };
    }
}

TypeScript Configuration

{
  "target": "es2015", // at least
  "experimentalDecorators": true
}

Contribution

Feel free to contribute by forking this repository, making, testing, and building your changes, then opening a pull request. Please try to maintain a uniform code style.