Performance
Caching
Add caching to any route with one line: automatic TTL, dynamic keys, and smart invalidation, with no manual key management.
Overview
Without caching, every request hits your database or expensive operations. With caching, repeated responses are served instantly, and MoroJS handles the keys, expiration, and invalidation for you.
1app.get('/users')
2 .cache({ ttl: 60 }) // Cache for 60 seconds
3 .handler(() => {
4 // This only runs if not cached
5 return getAllUsers();
6 });Without caching
- Every request hits the database
- Slow response times
- High server load
- Manual cache management
With MoroJS
- Instant responses from cache
- Reduced database load
- Automatic TTL management
- One-line setup
Fast
Instant responses from cache. No database queries for cached data.
Automatic
TTL-based expiration. No manual cache management needed.
Simple
One line of code. Dynamic keys. Smart invalidation.
How It Works
MoroJS caching automatically stores responses based on cache keys and TTL (time-to-live) values. When a request comes in, it checks the cache first. If found, it returns immediately. If not, it executes the handler and stores the result.
Route-Level Caching
1import { createApp, z } from '@morojs/moro';
2
3const app = await createApp();
4
5// Simple caching with TTL
6app.get('/users')
7 .query(z.object({
8 limit: z.coerce.number().min(1).max(100).default(10),
9 search: z.string().optional()
10 }))
11 .cache({ ttl: 60, key: 'users-list' }) // Cache for 60 seconds
12 .handler((req, res) => {
13 // This will only run if not cached
14 const users = getAllUsers(req.query);
15 return { success: true, data: users };
16 });
17
18// Dynamic cache keys with parameters
19app.get('/users/:id')
20 .cache({ ttl: 300, key: (req) => `user-${req.params.id}` }) // 5 minutes
21 .handler((req, res) => {
22 const user = getUserById(req.params.id);
23 return { success: true, data: user };
24 });1// Fast-changing data - short cache
2app.get('/data/fast')
3 .cache({ ttl: 30 }) // 30 seconds
4 .handler(() => getFastChangingData());
5
6// Stable data - long cache
7app.get('/data/slow')
8 .cache({ ttl: 3600 }) // 1 hour
9 .handler(() => getSlowChangingData());Advanced Caching
For advanced use cases, you can integrate external caching solutions like Redis, implement tag-based invalidation, and manage cache manually.
1import { createCacheAdapter } from '@morojs/moro';
2
3// Hold your own adapter reference — there is no global cache accessor.
4const cache = createCacheAdapter('memory');
5
6app.post('/users')
7 .body(CreateUserSchema)
8 .handler(async (req, res) => {
9 const user = await createUser(req.body);
10
11 // Adapters key on strings; delete the entries a write invalidates.
12 await cache.del('users:list');
13 await cache.del(`users:${user.id}`);
14
15 res.json({ user });
16 });1import { createCacheAdapter, MemoryCacheAdapter } from '@morojs/moro';
2
3// 'memory' | 'redis' | 'file', or construct an adapter directly
4const cache = createCacheAdapter('redis', { url: process.env.REDIS_URL });
5const local = new MemoryCacheAdapter();
6
7// The full CacheAdapter surface
8await cache.set('key', { data: 'value' }, 3600); // ttl in seconds
9const cached = await cache.get('key');
10const present = await cache.exists('key');
11const remaining = await cache.ttl('key');
12await cache.del('key');
13await cache.clear();