Skip to content

6.2 cache return value

wiki[bot] edited this page Aug 23, 2026 · 1 revision

6.2. Cache Return Value

Decorates methods and getter properties to cache their return values with optional TTL-based expiration and Joi schema validation.

Import

import { CacheReturnValue } from '@triplef/config-factory/cache-return-value';

Usage

Basic Usage (Default 5 Minute TTL)

By default, cached values expire after 5 minutes and use a stale-while-revalidate strategy.

import { Injectable } from '@nestjs/common';
import { CacheReturnValue } from '@triplef/config-factory/cache-return-value';

@Injectable()
export class AppConfigService {
  @CacheReturnValue()
  get config() {
    console.log('Computing config...');
    return { port: 3000, env: 'development' };
  }
}

const service = new AppConfigService();
service.config; // Logs "Computing config..."
service.config; // Returns cached value (no log)

Permanent Cache

Use false to disable TTL expiration entirely.

@CacheReturnValue(false)
get permanentConfig() {
  return { version: '1.0.0' };
}

Custom TTL

Provide a custom TTL in milliseconds. Use a number for shorthand or a CacheConfig object for additional options.

// Number shorthand (60 seconds)
@CacheReturnValue(60000)
get userData() {
  return fetchUserData();
}

// Config object (60 seconds)
@CacheReturnValue({ ttl: 60000 })
get otherData() {
  return fetchOtherData();
}

With Joi Validation

import Joi from 'joi';

const UserSchema = Joi.object({
  id: Joi.number().required(),
  name: Joi.string().required(),
});

@CacheReturnValue(UserSchema)
getUserById(id: number) {
  return { id, name: 'John' };
}

getUserById(1); // Validates against UserSchema
getUserById(1); // Returns cached, validated value

Combining Schema and TTL

@CacheReturnValue({ schema: UserSchema, ttl: 300000 }) // 5 minutes with validation
getUserById(id: number) {
  return { id, name: 'John' };
}

Caching Methods

@CacheReturnValue()
getUserById(id: number) {
  console.log(`Fetching user ${id}...`);
  return { id, name: 'John' };
}

getUserById(1); // Logs "Fetching user 1..."
getUserById(1); // Returns cached value
getUserById(2); // Logs "Fetching user 2..." (different args)

Stale-While-Revalidate

When TTL expires, the cached value is returned immediately (non-blocking) while a background refresh is triggered. This ensures:

  • No blocking: The caller always receives a value immediately
  • Background refresh: The cache is updated asynchronously
  • Shared promise: Multiple concurrent reads share the same refresh promise, preventing duplicate fetches
@CacheReturnValue({ ttl: 1000 })
get data() {
  return fetchData(); // Called once when stale, not per concurrent read
}

// Request 1: Returns stale value, triggers refresh
// Request 2-10: Returns stale value, reuses in-flight refresh
// After refresh completes: All subsequent requests get fresh value

Features

Instance Isolation

Each class instance has its own cache. Different instances do not share cached values.

Method Argument Hashing

Method caches are keyed by a hash of the method name and serialized arguments, enabling separate caches for different argument combinations.

Automatic Validation

When a Joi schema is provided, return values are validated before caching. Invalid values throw ValidateReturnValueError.

TTL Options

Input TTL Behavior
@CacheReturnValue() 5 minutes (default)
@CacheReturnValue(schema) 5 minutes with validation
@CacheReturnValue(60000) 60 seconds, no validation
@CacheReturnValue(false) Permanent (no expiration)
@CacheReturnValue({ ttl: false }) Permanent with optional validation
@CacheReturnValue({ ttl: 60000 }) 60 seconds, no validation
@CacheReturnValue({ schema, ttl: 60000 }) 60 seconds with validation

Complete Example

import { Injectable } from '@nestjs/common';
import { CacheReturnValue } from '@triplef/config-factory/cache-return-value';
import Joi from 'joi';

const DatabaseConfigSchema = Joi.object({
  host: Joi.string().required(),
  port: Joi.number().port().required(),
  database: Joi.string().required(),
});

@Injectable()
export class DatabaseConfigService {
  // Default 5 minute TTL with validation
  @CacheReturnValue(DatabaseConfigSchema)
  get config() {
    return {
      host: process.env.DB_HOST || 'localhost',
      port: parseInt(process.env.DB_PORT || '5432'),
      database: process.env.DB_NAME || 'myapp',
    };
  }

  // Custom 1 minute TTL with validation
  @CacheReturnValue({ schema: Joi.object({ id: Joi.number().required() }), ttl: 60000 })
  async fetchUser(id: number) {
    return { id, name: 'User ' + id };
  }

  // Permanent cache (no TTL)
  @CacheReturnValue({ schema: Joi.string(), ttl: false })
  getVersion() {
    return '1.0.0';
  }
}

See Also

Clone this wiki locally