Overview
NestJS CRUD provides built-in caching capabilities for query results, helping you improve API performance by reducing database calls. Caching is particularly useful for:
- Frequently accessed data
- Complex queries with joins
- Read-heavy applications
- Reducing database load
Prerequisites
Caching requires TypeORM’s cache configuration. First, configure your TypeORM connection:
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeOrmConfig: TypeOrmModuleOptions = {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'root',
password: 'root',
database: 'myapp',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
// Enable caching
cache: {
duration: 30000, // 30 seconds default cache duration
},
};
TypeORM supports multiple cache providers including in-memory, Redis, and database caching.
Basic Configuration
Enable caching for specific controllers:
import { Controller } from '@nestjs/common';
import { Crud } from '@nestjsx/crud';
import { User } from './user.entity';
import { UsersService } from './users.service';
@Crud({
model: { type: User },
query: {
cache: 2000, // Cache for 2 seconds
},
})
@Controller('users')
export class UsersController {
constructor(public service: UsersService) {}
}
Global Configuration
Set default cache duration globally:
import { CrudConfigService } from '@nestjsx/crud';
CrudConfigService.load({
query: {
cache: 5000, // 5 seconds default for all controllers
},
});
Cache Duration
Specify cache duration in milliseconds:
@Crud({
model: { type: Product },
query: {
cache: 60000, // 1 minute
},
})
Common Durations
| Duration | Milliseconds | Use Case |
|---|
| 1 second | 1000 | Rapidly changing data |
| 5 seconds | 5000 | Frequently updated data |
| 30 seconds | 30000 | Moderately stable data |
| 1 minute | 60000 | Stable data |
| 5 minutes | 300000 | Rarely changing data |
| 1 hour | 3600000 | Configuration data |
Disabling Cache
Disable caching for specific controllers:
@Crud({
model: { type: Order },
query: {
cache: false, // No caching
},
})
Cache Control via Query Parameter
Clients can control caching using the cache query parameter:
Bypass Cache
This forces a fresh database query, bypassing any cached results.
Use Cache
This uses cached results if available (default behavior).
The cache query parameter allows clients to refresh stale data when needed.
Cache with TypeORM
In-Memory Caching
Default in-memory cache (development only):
export const typeOrmConfig: TypeOrmModuleOptions = {
// ... other options
cache: true, // Uses in-memory caching
};
Redis Caching
Production-ready caching with Redis:
npm install --save cache-manager cache-manager-redis-store
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
import * as redisStore from 'cache-manager-redis-store';
export const typeOrmConfig: TypeOrmModuleOptions = {
type: 'postgres',
// ... other options
cache: {
type: 'redis',
options: {
host: 'localhost',
port: 6379,
},
duration: 30000, // 30 seconds
},
};
Database Caching
Use database table for caching:
export const typeOrmConfig: TypeOrmModuleOptions = {
type: 'postgres',
// ... other options
cache: {
type: 'database',
tableName: 'query_result_cache',
duration: 30000,
},
};
Create the cache table:
CREATE TABLE query_result_cache (
id SERIAL PRIMARY KEY,
identifier VARCHAR(255),
time BIGINT NOT NULL,
duration INTEGER NOT NULL,
query TEXT NOT NULL,
result TEXT NOT NULL
);
Cache Behavior
What Gets Cached
The following operations are cached:
getMany - List queries
getOne - Single entity retrieval
Cache Keys
Cache keys are automatically generated based on:
- Entity type
- Query parameters (filters, joins, sort, etc.)
- Field selection
- Pagination settings
Different query parameters result in different cache entries.
Cache Invalidation
Cache is automatically invalidated when:
- Cache duration expires
- Client requests
?cache=0
- You manually clear the cache (see below)
Manual Cache Control
Clear cache programmatically:
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { TypeOrmCrudService } from '@nestjsx/crud-typeorm';
import { Repository } from 'typeorm';
import { User } from './user.entity';
@Injectable()
export class UsersService extends TypeOrmCrudService<User> {
constructor(
@InjectRepository(User)
private userRepo: Repository<User>
) {
super(userRepo);
}
async clearCache() {
// Clear query result cache
await this.userRepo.manager.connection.queryResultCache.clear();
}
async clearSpecificCache(identifier: string) {
// Clear specific cache entry
await this.userRepo.manager.connection.queryResultCache.remove([identifier]);
}
}
Complete Example
Here’s a production-ready caching setup:
TypeORM Configuration
import { TypeOrmModuleOptions } from '@nestjs/typeorm';
export const typeOrmConfig: TypeOrmModuleOptions = {
type: 'postgres',
host: process.env.DB_HOST || 'localhost',
port: parseInt(process.env.DB_PORT) || 5432,
username: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD || 'root',
database: process.env.DB_NAME || 'myapp',
entities: [__dirname + '/**/*.entity{.ts,.js}'],
synchronize: false,
logging: process.env.NODE_ENV === 'development',
cache: {
type: 'redis',
options: {
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT) || 6379,
},
duration: 30000, // 30 seconds default
},
};
App Module
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { typeOrmConfig } from './orm.config';
import { UsersModule } from './users/users.module';
import { ProductsModule } from './products/products.module';
@Module({
imports: [
TypeOrmModule.forRoot(typeOrmConfig),
UsersModule,
ProductsModule,
],
})
export class AppModule {}
Controller with Caching
import { Controller } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Crud } from '@nestjsx/crud';
import { Product } from './product.entity';
import { ProductsService } from './products.service';
@Crud({
model: { type: Product },
query: {
cache: 60000, // Cache product queries for 1 minute
join: {
category: {
eager: true,
},
reviews: {},
},
},
})
@ApiTags('products')
@Controller('products')
export class ProductsController {
constructor(public service: ProductsService) {}
}
Global Configuration
import { NestFactory } from '@nestjs/core';
import { CrudConfigService } from '@nestjsx/crud';
import { AppModule } from './app.module';
CrudConfigService.load({
query: {
cache: 30000, // 30 seconds default cache
},
});
async function bootstrap() {
const app = await NestFactory.create(AppModule);
await app.listen(3000);
}
bootstrap();
Cache Hit Ratio
Monitor cache effectiveness:
import { Logger } from '@nestjs/common';
@Injectable()
export class CacheMonitorService {
private logger = new Logger('CacheMonitor');
async getCacheStats() {
// Implementation depends on cache provider
// For Redis:
const info = await redisClient.info('stats');
this.logger.log(`Cache stats: ${info}`);
}
}
Memory Usage
In-memory caching can consume significant memory. Use Redis or database caching for production applications.
Cache Warming
Pre-populate cache on startup:
import { Injectable, OnModuleInit } from '@nestjs/common';
@Injectable()
export class CacheWarmupService implements OnModuleInit {
constructor(private productsService: ProductsService) {}
async onModuleInit() {
// Warm up frequently accessed data
await this.productsService.findAll();
}
}
Best Practices
Choose Appropriate Duration
Set cache duration based on how frequently data changes. Frequently updated data should have shorter cache times.
Use Redis in Production
Always use Redis or another external cache provider in production environments.
Monitor Cache Performance
Track cache hit rates and adjust durations accordingly.
Implement Cache Invalidation
Clear cache when data is updated to prevent serving stale data.
Consider Memory Limits
Set appropriate memory limits for your cache provider to prevent out-of-memory issues.
Troubleshooting
Cache Not Working
If caching isn’t working:
- Verify TypeORM cache is configured
- Check that
cache option is set in query configuration
- Ensure cache provider (Redis/database) is running
- Verify no
?cache=0 parameter is being sent
Stale Data Issues
If you’re seeing stale data:
- Reduce cache duration
- Implement cache invalidation on updates
- Use
?cache=0 to bypass cache when needed
- Clear cache after write operations
Memory Issues
If experiencing memory problems:
- Switch from in-memory to Redis caching
- Reduce cache duration
- Set Redis
maxmemory policy
- Monitor and clear old cache entries