Decorators

Caching Decorators

Define TypeScript caching strategies, eviction policies, cache updates, invalidation, and stampede protection with AxilJS semantic decorators.

4 min readDocumentationEdit this page

Caching Decorators

Caching decorators define how method results are cached, updated, evicted, and protected from concurrent cache misses. They keep cache strategy separate from business logic while exposing the intended semantics to independent consumers.

@tCache

@tCache caches a method's return value under a specified key with a time-to-live (TTL).

typescript
import { tCache } from '@axiljs/decorator'
 
@tCache({
  key: (id) => `user:${id}`,
  ttl: 60_000,
  tags: ['users'],
})
async getUser(id: string) {
  return this.userRepository.findById(id)
}

Options

OptionTypeDescription
keystring | FunctionCache key
ttlnumberTime to live in milliseconds
tagsstring[]Tags used for group invalidation
conditionFunctionPredicate that determines whether the result should be cached

These options allow cache identity, expiration, grouping, and conditional caching to be declared independently of the method implementation.

@tCacheEvict

@tCacheEvict removes cached entries when the decorated method executes.

Use a specific key when a single cache entry must be removed:

typescript
@tCacheEvict({
  key: (id) => `user:${id}`,
})
async updateUser(id: string) {
  return this.userRepository.update(id)
}

You can also evict entries associated with a tag:

typescript
@tCacheEvict({
  tags: ['users'],
})
async deleteUser(id: string) {
  return this.userRepository.delete(id)
}

@tCachePut

@tCachePut updates the cache using the decorated method's return value.

typescript
@tCachePut({
  key: (id) => `user:${id}`,
  ttl: 60_000,
})
async refreshUser(id: string) {
  return this.userRepository.findById(id)
}

This expresses an explicit cache-refresh operation rather than relying on a cache read to populate the entry.

@tCacheInvalidate

@tCacheInvalidate invalidates all cache entries associated with the specified tags.

typescript
@tCacheInvalidate('products', 'categories')
async bulkUpdate() {
  await this.productRepository.updateMany()
  await this.categoryRepository.updateMany()
}

This is useful when one operation affects multiple cached resources and invalidating individual keys would be impractical.

@tCacheStampede

A cache stampede can occur when an expired or missing cache entry causes many concurrent requests to recompute the same expensive result.

@tCacheStampede declares how those concurrent misses should be handled:

typescript
@tCache({
  key: (id) => `product:${id}`,
  ttl: 60_000,
})
@tCacheStampede({
  strategy: 'single-flight',
})
async getProduct(id: string) {
  return this.productRepository.findById(id)
}

AxilJS defines three stampede-protection strategies:

StrategyDescription
'single-flight'Coalesce concurrent cache misses into one call
'lock'Acquire a lock before recomputing the value
'stale-while-revalidate'Serve stale data while refreshing it in the background

Combining Cache Semantics

Caching decorators can be composed with HTTP and database semantics to describe the complete behavior of an operation.

typescript
@tHttp({ method: 'GET', path: '/products/:id' })
@tCache({
  key: (id) => `product:${id}`,
  ttl: 300_000,
  tags: ['products'],
})
@tCacheStampede({
  strategy: 'single-flight',
})
@tDatabase({ operation: 'read' })
async getProduct(@tParam('id') id: string) {
  return this.productRepo.findById(id)
}
 
@tHttp({ method: 'PUT', path: '/products/:id' })
@tCacheEvict({
  key: (id) => `product:${id}`,
  tags: ['products'],
})
@tDatabase({ operation: 'write' })
async updateProduct(
  @tParam('id') id: string,
  @tBody() dto: UpdateDto,
) {
  return this.productRepo.update(id, dto)
}

The first method declares a cached database read with single-flight stampede protection. The second declares a write that evicts the affected cache entry and associated product entries.

Choosing the Right Decorator

Use @tCache when the result should be cached with a key and TTL.

Use @tCacheEvict when an operation should remove existing cache entries.

Use @tCachePut when an operation should update the cache with its returned value.

Use @tCacheInvalidate when multiple entries should be invalidated through tags.

Use @tCacheStampede when concurrent cache misses could trigger expensive duplicate work.

These decorators describe cache intent. The actual cache implementation is handled by the consumer that interprets the metadata, keeping caching semantics separate from application business logic.

Reference

DecoratorPurpose
tCacheCache method results with a key and TTL
tCacheEvictRemove cached entries
tCachePutUpdate the cache with a method result
tCacheInvalidateInvalidate entries by tags
tCacheStampedeProtect against concurrent cache misses

Caching is one part of AxilJS's broader semantic decorator model. The decorator writes metadata; independent consumers interpret that metadata and implement the corresponding runtime behavior.

Help improve the documentation

AxilJS is open source and documentation improvements are welcome.

AxilJS DocumentationMIT License · Built by SyntaxilitY