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 🙏

© 2026 – Pkg Stats / Ryan Hefner

@sourceloop/cache

v7.1.1

Published

A caching extension to cache methods

Readme

caching-guard

A Caching component that provides helpers for caching in Loopback4 based microservices.

LoopBack

Installation

Install CachingComponent using npm;

$ [npm install | yarn add] @sourceloop/cache

Basic Usage

Configure and load CachingComponent in the application constructor as shown below.

import {CachingComponent} from '@sourceloop/cache';
// ...
export class MyApplication extends BootMixin(
  ServiceMixin(RepositoryMixin(RestApplication)),
) {
  constructor(options: ApplicationConfig = {}) {
    this.bind(CacheComponentBindings.CacheConfig).to({
      ttl: 86400000, // 1 day (default)
      strategy: RedisStoreStrategy,
      datasourceName: 'redisCacheStore',
    });
    this.component(CachingComponent);
    // ...
  }
  // ...
}

In a repository

To add caching to a repository, just add it as a mixin to the base class -

export class TestWithMixinRepository extends CacheMixin(
  DefaultCrudRepository<Test, number, {}>,
) {
  cacheIdentifier = 'testRepo';
  constructor(@inject('datasources.memorydb') dataSource: juggler.DataSource) {
    super(Test, dataSource);
  }
}

Using with Sequelize Repositories

For repositories based on SequelizeCrudRepository from @loopback/sequelize, use the SequelizeCacheMixin:

import {SequelizeCacheMixin} from '@sourceloop/cache/sequelize';
import {SequelizeCrudRepository, SequelizeDataSource} from '@loopback/sequelize';

export class UserRepository extends SequelizeCacheMixin(
  SequelizeCrudRepository<User, number, {}>,
  {
    ttl: 1800000,                 // Time to live in milliseconds (optional, 30 minutes)
    invalidationTags: ['users'],   // Tags for cache invalidation on writes
    cachedItemTags: ['user-item'], // Tags for individual cache entries
    disableCachedFetch: false,     // Disable caching if needed (optional)
  },
) {
  cacheIdentifier = 'userRepo';   // Required: unique cache namespace

  constructor(
    @inject('datasources.postgres') dataSource: SequelizeDataSource,
  ) {
    super(User, dataSource);
  }
}

Note: Import from @sourceloop/cache/sequelize for Sequelize-specific caching.

Difference from CacheMixin:

  • CacheMixin works with DefaultCrudRepository (Juggler-based)
  • SequelizeCacheMixin works with SequelizeCrudRepository (Sequelize-based)
  • Use the appropriate mixin based on your repository type

Configuration Options:

| Option | Type | Default | Description | |--------|------|---------|-------------| | ttl | number | 86400000 (1 day) | Cache expiration time in milliseconds | | invalidationTags | string[] | [] | Tags to invalidate on create/update/delete | | cachedItemTags | string[] | [] | Tags to apply to cached items | | disableCachedFetch | boolean | false | Bypass cache for all reads |

In a controller or service

To add caching to a service or controller, just implement the ICachedService interface, adding a binding for the ICacheService and the applying the relevant decorators to the methods you want cached -

export class TestController implements ICachedService {
  constructor(
    @repository(TestWithoutCachingRepository)
    public testModelRepository: TestWithoutCachingRepository,
    @inject(CacheComponentBindings.CacheService)
    public cache: ICacheService,
  ) {}
  cacheIdentifier = 'testRepo';

  @cacheInvalidator()
  @post('/tests')
  @response(200, {
    description: 'Test model instance',
    content: {'application/json': {schema: getModelSchemaRef(Test)}},
  })
  async create(
    @requestBody({
      content: {
        'application/json': {
          schema: getModelSchemaRef(Test, {
            title: 'NewTest',
            exclude: ['id'],
          }),
        },
      },
    })
    testModel: Omit<Test, 'id'>,
  ): Promise<Test> {
    return this.testModelRepository.create(testModel);
  }
  // ...
  @cachedItem()
  @get('/tests/count')
  @response(200, {
    description: 'Test model count',
    content: {'application/json': {schema: CountSchema}},
  })
  async count(@param.where(Test) where?: Where<Test>): Promise<Count> {
    return this.testModelRepository.count(where);
  }
  /// ...

Advanced Usage

Cache Strategies

The component supports two cache strategies:

1. InMemoryStoreStrategy (Default)

Uses an in-memory Map. Suitable for development and single-instance deployments.

import {InMemoryStoreStrategy} from '@sourceloop/cache';

this.bind(CacheComponentBindings.CacheConfig).to({
  ttl: 3600000, // 1 hour
  strategy: InMemoryStoreStrategy,
});

2. RedisStoreStrategy

Uses Redis for distributed caching. Recommended for production.

import {RedisStoreStrategy} from '@sourceloop/cache';
import {juggler} from '@loopback/repository';

const redisDs = new juggler.DataSource({
  name: 'redisCacheStore',
  connector: 'kv-redis',
  host: process.env.REDIS_HOST,
  port: Number(process.env.REDIS_PORT),
  password: process.env.REDIS_PASSWORD,
});

this.dataSource(redisDs);

this.bind(CacheComponentBindings.CacheConfig).to({
  ttl: 3600000, // 1 hour
  strategy: RedisStoreStrategy,
  datasourceName: 'redisCacheStore',
});

Cache Invalidation

Cache can be invalidated in two ways:

1. Automatic Invalidation (Mixins)

Write operations (create, update, delete) automatically invalidate cache entries tagged with invalidationTags.

2. Manual Invalidation (Decorators)

Use @cacheInvalidator() decorator on methods that modify data:

@cacheInvalidator(['users', 'notifications'])
async updateUserStatus(userId: number, status: string) {
  // This will invalidate all cache entries tagged with 'users' or 'notifications'
  return this.userRepository.updateById(userId, {status});
}

Cache Customization

Per-Method Options

Override cache behavior for specific method calls:

// Force fresh data from database
const users = await this.userRepository.find(undefined, {
  forceUpdate: true,
});

// Add custom tags
const user = await this.userRepository.findById(1, undefined, {
  tags: ['admin-user', 'high-priority'],
});

Tenant-Aware Caching

Cache keys automatically include tenant ID when AUTH_USER_KEY is bound:

import {AUTH_USER_KEY} from '@sourceloop/cache';

this.bind(AUTH_USER_KEY).to({
  tenantId: 'tenant-123',
  username: 'admin',
});

API Reference

Decorators

@cachedItem()

Cache the return value of a method.

@cachedItem()
async getUser(id: number): Promise<User> {
  return this.userRepository.findById(id);
}

@cacheInvalidator(tags: string[])

Invalidate cache entries matching the given tags.

@cacheInvalidator(['users'])
async updateUser(id: number, data: Partial<User>): Promise<User> {
  return this.userRepository.updateById(id, data);
}

Interfaces

ICacheMixinOptions

Configuration options for cache mixins.

interface ICacheMixinOptions {
  ttl?: number;                    // Time to live in milliseconds
  invalidationTags?: string[];     // Tags for write operations
  cachedItemTags?: string[];       // Tags for read operations
  disableCachedFetch?: boolean;    // Disable caching
}

ICachedMethodOptions

Options for individual method calls.

interface ICachedMethodOptions {
  forceUpdate?: boolean;   // Force fresh data from source
  tags?: string[];         // Additional cache tags
}

Migration from CacheMixin to SequelizeCacheMixin

If you're migrating from Juggler to Sequelize repositories:

  1. Update import:

    // Before
    import {CacheMixin} from '@sourceloop/cache';
    
    // After
    import {SequelizeCacheMixin} from '@sourceloop/cache/sequelize';
  2. Update repository base:

    // Before
    export class UserRepo extends CacheMixin(
      DefaultCrudRepository<User, number, {}>,
      options,
    ) { ... }
    
    // After
    export class UserRepo extends SequelizeCacheMixin(
      SequelizeCrudRepository<User, number, {}>,
      options,
    ) { ... }
  3. Update constructor:

    // Before
    constructor(@inject('datasources.memorydb') dataSource: juggler.DataSource) { ... }
    
    // After
    constructor(@inject('datasources.postgres') dataSource: SequelizeDataSource) { ... }

License

MIT