feat: implement API response caching with node-cache
- Add in-memory cache middleware with configurable TTL - Cache GET endpoints: streets (5min), events (2min), posts (1min), rewards (10min) - Automatic cache invalidation on POST/PUT/DELETE operations - Add cache statistics endpoint (GET /api/cache/stats) - Add cache management endpoint (DELETE /api/cache) - Cache hit rate tracking and monitoring - Pattern-based cache invalidation - Optimized for Raspberry Pi deployment (lightweight in-memory) 🤖 Generated with Claude Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
43
backend/routes/cache.js
Normal file
43
backend/routes/cache.js
Normal file
@@ -0,0 +1,43 @@
|
||||
const express = require("express");
|
||||
const auth = require("../middleware/auth");
|
||||
const { asyncHandler } = require("../middleware/errorHandler");
|
||||
const { getCacheStats, clearCache } = require("../middleware/cache");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
/**
|
||||
* GET /api/cache/stats
|
||||
* Get cache statistics
|
||||
* @access Private (authenticated users)
|
||||
*/
|
||||
router.get(
|
||||
"/stats",
|
||||
auth,
|
||||
asyncHandler(async (req, res) => {
|
||||
const stats = getCacheStats();
|
||||
res.json({
|
||||
status: "operational",
|
||||
...stats,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* DELETE /api/cache
|
||||
* Clear all cache
|
||||
* @access Private (authenticated users - in production should be admin only)
|
||||
*/
|
||||
router.delete(
|
||||
"/",
|
||||
auth,
|
||||
asyncHandler(async (req, res) => {
|
||||
clearCache();
|
||||
res.json({
|
||||
msg: "Cache cleared successfully",
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
})
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
@@ -9,6 +9,7 @@ const {
|
||||
} = require("../middleware/validators/eventValidator");
|
||||
const { paginate, buildPaginatedResponse } = require("../middleware/pagination");
|
||||
const couchdbService = require("../services/couchdbService");
|
||||
const { getCacheMiddleware, invalidateCacheByPattern } = require("../middleware/cache");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -16,6 +17,7 @@ const router = express.Router();
|
||||
router.get(
|
||||
"/",
|
||||
paginate,
|
||||
getCacheMiddleware(120), // Cache for 2 minutes
|
||||
asyncHandler(async (req, res) => {
|
||||
const { page, limit } = req.pagination;
|
||||
|
||||
@@ -50,6 +52,9 @@ router.post(
|
||||
location,
|
||||
});
|
||||
|
||||
// Invalidate events cache
|
||||
invalidateCacheByPattern('/api/events');
|
||||
|
||||
res.json(event);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -10,6 +10,7 @@ const {
|
||||
const { upload, handleUploadError } = require("../middleware/upload");
|
||||
const { uploadImage, deleteImage } = require("../config/cloudinary");
|
||||
const { paginate, buildPaginatedResponse } = require("../middleware/pagination");
|
||||
const { getCacheMiddleware, invalidateCacheByPattern } = require("../middleware/cache");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -17,6 +18,7 @@ const router = express.Router();
|
||||
router.get(
|
||||
"/",
|
||||
paginate,
|
||||
getCacheMiddleware(60), // Cache for 1 minute
|
||||
asyncHandler(async (req, res) => {
|
||||
const { skip, limit, page } = req.pagination;
|
||||
|
||||
@@ -58,6 +60,9 @@ router.post(
|
||||
|
||||
const post = await Post.create(postData);
|
||||
|
||||
// Invalidate posts cache
|
||||
invalidateCacheByPattern('/api/posts');
|
||||
|
||||
res.json({
|
||||
post,
|
||||
pointsAwarded: 5, // Standard post creation points
|
||||
|
||||
@@ -7,6 +7,7 @@ const {
|
||||
rewardIdValidation,
|
||||
} = require("../middleware/validators/rewardValidator");
|
||||
const { paginate, buildPaginatedResponse } = require("../middleware/pagination");
|
||||
const { getCacheMiddleware, invalidateCacheByPattern } = require("../middleware/cache");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -14,6 +15,7 @@ const router = express.Router();
|
||||
router.get(
|
||||
"/",
|
||||
paginate,
|
||||
getCacheMiddleware(600), // Cache for 10 minutes
|
||||
asyncHandler(async (req, res) => {
|
||||
const { page, limit } = req.pagination;
|
||||
|
||||
@@ -37,6 +39,9 @@ router.post(
|
||||
isPremium,
|
||||
});
|
||||
|
||||
// Invalidate rewards cache
|
||||
invalidateCacheByPattern('/api/rewards');
|
||||
|
||||
res.json(reward);
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -8,12 +8,14 @@ const {
|
||||
createStreetValidation,
|
||||
streetIdValidation,
|
||||
} = require("../middleware/validators/streetValidator");
|
||||
const { getCacheMiddleware, invalidateCacheByPattern } = require("../middleware/cache");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
// Get all streets (with pagination and filtering)
|
||||
router.get(
|
||||
"/",
|
||||
getCacheMiddleware(300), // Cache for 5 minutes
|
||||
asyncHandler(async (req, res) => {
|
||||
const { buildPaginatedResponse } = require("../middleware/pagination");
|
||||
|
||||
@@ -81,6 +83,7 @@ router.get(
|
||||
router.get(
|
||||
"/:id",
|
||||
streetIdValidation,
|
||||
getCacheMiddleware(300), // Cache for 5 minutes
|
||||
asyncHandler(async (req, res) => {
|
||||
const street = await Street.findById(req.params.id);
|
||||
if (!street) {
|
||||
@@ -109,6 +112,9 @@ router.post(
|
||||
location,
|
||||
});
|
||||
|
||||
// Invalidate streets cache
|
||||
invalidateCacheByPattern('/api/streets');
|
||||
|
||||
res.json(street);
|
||||
}),
|
||||
);
|
||||
@@ -156,6 +162,9 @@ router.put(
|
||||
user.stats.streetsAdopted = user.adoptedStreets.length;
|
||||
await user.save();
|
||||
|
||||
// Invalidate streets cache
|
||||
invalidateCacheByPattern('/api/streets');
|
||||
|
||||
// Award points for street adoption using CouchDB service
|
||||
const updatedUser = await couchdbService.updateUserPoints(
|
||||
req.user.id,
|
||||
|
||||
Reference in New Issue
Block a user