Performance Tuning
Optimize your MoroJS applications for maximum performance. Learn caching strategies, database optimization, and monitoring techniques for production workloads.
Performance Monitoring
Simple Performance Monitoring
typescript
1import { createApp } from '@morojs/moro';
2
3const app = createApp({
4 cors: true,
5 compression: true, // Built-in performance feature
6 helmet: true
7});
8
9// Simple performance middleware (custom implementation)
10app.use((req, res, next) => {
11 const start = Date.now();
12
13 // Add request timing
14 req.startTime = start;
15
16 // Override res.json to add timing header
17 const originalJson = res.json;
18 res.json = function(data) {
19 const duration = Date.now() - start;
20 res.setHeader('X-Response-Time', `${duration}ms`);
21
22 // Log slow requests
23 if (duration > 1000) {
24 console.warn(`Slow request: ${req.method} ${req.path} - ${duration}ms`);
25 }
26
27 return originalJson.call(this, data);
28 };
29
30 next();
31});
32
33// Health check with performance metrics
34app.get('/health', (req, res) => {
35 const memUsage = process.memoryUsage();
36
37 return {
38 status: 'healthy',
39 timestamp: new Date().toISOString(),
40 uptime: process.uptime(),
41 memory: {
42 rss: Math.round(memUsage.rss / 1024 / 1024) + 'MB',
43 heapTotal: Math.round(memUsage.heapTotal / 1024 / 1024) + 'MB',
44 heapUsed: Math.round(memUsage.heapUsed / 1024 / 1024) + 'MB'
45 },
46 nodeVersion: process.version
47 };
48});
49
50// Performance endpoint from feature-showcase
51app.get('/config', (req, res) => {
52 const config = app.getConfig();
53 return {
54 success: true,
55 data: {
56 environment: config.server.environment,
57 compression: config.security.compression,
58 caching: {
59 enabled: config.modules.cache.enabled,
60 defaultTtl: config.modules.cache.defaultTtl
61 }
62 }
63 };
64});
Advanced Caching Strategies
Simple Caching with MoroJS
typescript
1// Built-in caching with chainable API (actual implementation)
2app.get('/users')
3 .query(z.object({
4 limit: z.coerce.number().default(10),
5 search: z.string().optional()
6 }))
7 .cache({ ttl: 60, key: 'users-list' }) // Cache for 60 seconds
8 .handler((req, res) => {
9 const users = getAllUsers(req.query);
10 return { success: true, data: users };
11 });
12
13// Dynamic cache keys
14app.get('/users/:id')
15 .cache({ ttl: 300, key: (req) => `user-${req.params.id}` }) // 5 minutes
16 .handler((req, res) => {
17 const user = getUserById(req.params.id);
18 return { success: true, data: user };
19 });
20
21// Configuration-driven caching
22app.get('/data')
23 .cache({ ttl: app.getConfig().modules.cache.defaultTtl })
24 .handler((req, res) => {
25 return { data: getExpensiveData() };
26 });
27
28// External Redis caching (for advanced use cases)
29import Redis from 'redis';
30
31const redis = Redis.createClient({ url: process.env.REDIS_URL });
32await redis.connect();
33
34// Custom Redis middleware
35const redisCache = (ttl) => {
36 return async (req, res, next) => {
37 const cacheKey = `cache:${req.method}:${req.path}`;
38
39 try {
40 const cached = await redis.get(cacheKey);
41 if (cached) {
42 res.setHeader('X-Cache', 'HIT');
43 return res.json(JSON.parse(cached));
44 }
45
46 const originalJson = res.json;
47 res.json = function(data) {
48 redis.setEx(cacheKey, ttl, JSON.stringify(data));
49 res.setHeader('X-Cache', 'MISS');
50 return originalJson.call(this, data);
51 };
52
53 next();
54 } catch (error) {
55 console.error('Cache error:', error);
56 next();
57 }
58 };
59};
60
61// Use Redis caching
62app.get('/expensive-data', redisCache(300), (req, res) => {
63 const data = performExpensiveOperation();
64 return { success: true, data };
65});
Database Optimization
Query Optimization
typescript
1// Query performance monitoring
2const queryMonitor = {
3 slowQueryThreshold: 1000, // 1 second
4
5 async logSlowQuery(query: string, params: any[], duration: number) {
6 if (duration > this.slowQueryThreshold) {
7 console.warn('Slow query detected:', {
8 query,
9 params,
10 duration: `${duration}ms`,
11 timestamp: new Date().toISOString()
12 });
13
14 // Send to monitoring service
15 await sendMetric('slow_query', {
16 query_hash: hashQuery(query),
17 duration,
18 timestamp: Date.now()
19 });
20 }
21 }
22};
23
24// Optimized database middleware
25app.use(async (context, next) => {
26 const originalQuery = context.db.query;
27
28 context.db.query = async (sql: string, params: any[] = []) => {
29 const start = Date.now();
30
31 try {
32 const result = await originalQuery.call(context.db, sql, params);
33 const duration = Date.now() - start;
34
35 await queryMonitor.logSlowQuery(sql, params, duration);
36
37 return result;
38 } catch (error) {
39 console.error('Database query error:', { sql, params, error: error.message });
40 throw error;
41 }
42 };
43
44 await next();
45});
46
47// Connection pool optimization
48app.get('/users/:id', {
49 handler: async ({ params, db }) => {
50 // Use read replica for read-only queries
51 const user = await db.replica.query(
52 'SELECT id, name, email FROM users WHERE id = $1',
53 [params.id]
54 );
55
56 if (!user[0]) {
57 return { error: 'User not found' };
58 }
59
60 // Use cache for frequently accessed data
61 const cacheKey = `user_profile:${params.id}`;
62 let profile = await cache.get(cacheKey);
63
64 if (!profile) {
65 profile = await db.replica.query(
66 'SELECT bio, avatar_url, preferences FROM user_profiles WHERE user_id = $1',
67 [params.id]
68 );
69
70 await cache.set(cacheKey, profile[0], { ttl: '15m' });
71 }
72
73 return {
74 ...user[0],
75 profile: profile[0]
76 };
77 }
78});