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.

Add caching with one line

typescript

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

Basic Route Caching

typescript

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  });

Different TTLs for Different Data

typescript

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.

Tag-Based Invalidation

typescript

1import { getCache } from '@morojs/moro';
2
3app.post('/users', {
4  body: CreateUserSchema,
5  handler: async ({ body }) => {
6    const user = await createUser(body);
7
8    // Invalidate user-related caches
9    const cache = getCache();
10    await cache.invalidateByTag(['users']);
11
12    return { user };
13  }
14});

Manual Cache Management

typescript

1import { getCache } from '@morojs/moro';
2
3const cache = getCache();
4
5// Set cache manually
6await cache.set('key', { data: 'value' }, { ttl: '1h' });
7
8// Get from cache
9const cached = await cache.get('key');
10
11// Delete specific key
12await cache.delete('key');
13
14// Get cache stats
15const stats = await cache.getStats();
16console.log(`Hit rate: ${stats.hitRate}%`);

Next Steps