# 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 ```typescript 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. ```typescript 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. ```typescript @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. ```typescript // Number shorthand (60 seconds) @CacheReturnValue(60000) get userData() { return fetchUserData(); } // Config object (60 seconds) @CacheReturnValue({ ttl: 60000 }) get otherData() { return fetchOtherData(); } ``` ### With Joi Validation ```typescript 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 ```typescript @CacheReturnValue({ schema: UserSchema, ttl: 300000 }) // 5 minutes with validation getUserById(id: number) { return { id, name: 'John' }; } ``` ### Caching Methods ```typescript @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 ```typescript @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 ```typescript 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 - [6.3. Validate Return Value](6.3-validate-return-value.md) - Standalone validation decorator - [6.1. Config-Factory Module](6.1-config-factory.md) - Module for registering config services